void AEFVUpwindInternalSideFlux::calcJacobian(unsigned int /*iside*/, dof_id_type /*ielem*/, dof_id_type /*ineig*/, const std::vector<Real> & libmesh_dbg_var(uvec1), const std::vector<Real> & libmesh_dbg_var(uvec2), const RealVectorValue & dwave, DenseMatrix<Real> & jac1, DenseMatrix<Real> & jac2) const { mooseAssert(uvec1.size() == 1, "Invalid size for uvec1. Must be single variable coupling."); mooseAssert(uvec2.size() == 1, "Invalid size for uvec1. Must be single variable coupling."); // assign the size of Jacobian matrix, e.g. = (1, 1) for the advection equation jac1.resize(1, 1); jac2.resize(1, 1); // assume a constant velocity on the left RealVectorValue uadv1(1.0, 1.0, 1.0); // assume a constant velocity on the right RealVectorValue uadv2(1.0, 1.0, 1.0); // normal velocity on the left and right Real vdon1 = uadv1 * dwave; Real vdon2 = uadv2 * dwave; // calculate the so-called a^plus and a^minus Real aplus = 0.5 * (vdon1 + std::abs(vdon1)); Real amins = 0.5 * (vdon2 - std::abs(vdon2)); // finally calculate the Jacobian matrix jac1(0, 0) = aplus; jac2(0, 0) = amins; }
void DenseMatrix<T>::_svd_lapack (DenseVector<T>& sigma, DenseMatrix<T>& U, DenseMatrix<T>& VT) { // The calling sequence for dgetrf is: // DGESVD( JOBU, JOBVT, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, LWORK, INFO ) // JOBU (input) CHARACTER*1 // Specifies options for computing all or part of the matrix U: // = 'A': all M columns of U are returned in array U: // = 'S': the first min(m,n) columns of U (the left singular // vectors) are returned in the array U; // = 'O': the first min(m,n) columns of U (the left singular // vectors) are overwritten on the array A; // = 'N': no columns of U (no left singular vectors) are // computed. char JOBU = 'S'; // JOBVT (input) CHARACTER*1 // Specifies options for computing all or part of the matrix // V**T: // = 'A': all N rows of V**T are returned in the array VT; // = 'S': the first min(m,n) rows of V**T (the right singular // vectors) are returned in the array VT; // = 'O': the first min(m,n) rows of V**T (the right singular // vectors) are overwritten on the array A; // = 'N': no rows of V**T (no right singular vectors) are // computed. char JOBVT = 'S'; std::vector<T> sigma_val; std::vector<T> U_val; std::vector<T> VT_val; _svd_helper(JOBU, JOBVT, sigma_val, U_val, VT_val); // Load the singular values into sigma, ignore U_val and VT_val sigma.resize(sigma_val.size()); for(unsigned int i=0; i<sigma_val.size(); i++) sigma(i) = sigma_val[i]; int M = this->n(); int N = this->m(); int min_MN = (M < N) ? M : N; U.resize(M,min_MN); for(unsigned int i=0; i<U.m(); i++) for(unsigned int j=0; j<U.n(); j++) { unsigned int index = i + j*U.n(); // Column major storage U(i,j) = U_val[index]; } VT.resize(min_MN,N); for(unsigned int i=0; i<VT.m(); i++) for(unsigned int j=0; j<VT.n(); j++) { unsigned int index = i + j*U.n(); // Column major storage VT(i,j) = VT_val[index]; } }
void assemble_poisson(EquationSystems & es, const std::string & system_name) { libmesh_assert_equal_to (system_name, "Poisson"); const MeshBase & mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>("Poisson"); const DofMap & dof_map = system.get_dof_map(); FEType fe_type = dof_map.variable_type(0); UniquePtr<FEBase> fe (FEBase::build(dim, fe_type)); QGauss qrule (dim, FIFTH); fe->attach_quadrature_rule (&qrule); UniquePtr<FEBase> fe_face (FEBase::build(dim, fe_type)); QGauss qface(dim-1, FIFTH); fe_face->attach_quadrature_rule (&qface); const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<std::vector<Real> > & phi = fe->get_phi(); const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi(); DenseMatrix<Number> Ke; DenseVector<Number> Fe; std::vector<dof_id_type> dof_indices; 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) { const Elem * elem = *el; dof_map.dof_indices (elem, dof_indices); fe->reinit (elem); Ke.resize (dof_indices.size(), dof_indices.size()); Fe.resize (dof_indices.size()); for (unsigned int qp=0; qp<qrule.n_points(); qp++) { for (unsigned int i=0; i<phi.size(); i++) { Fe(i) += JxW[qp]*phi[i][qp]; for (unsigned int j=0; j<phi.size(); j++) Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]); } } dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); } }
void AssembleOptimization::assemble_A_and_F() { A_matrix->zero(); F_vector->zero(); const MeshBase & mesh = _sys.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); const unsigned int u_var = _sys.variable_number ("u"); const DofMap & dof_map = _sys.get_dof_map(); FEType fe_type = dof_map.variable_type(u_var); UniquePtr<FEBase> fe (FEBase::build(dim, fe_type)); QGauss qrule (dim, fe_type.default_quadrature_order()); fe->attach_quadrature_rule (&qrule); const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<std::vector<Real> > & phi = fe->get_phi(); const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi(); std::vector<dof_id_type> dof_indices; DenseMatrix<Number> Ke; DenseVector<Number> Fe; 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) { const Elem * elem = *el; dof_map.dof_indices (elem, dof_indices); const unsigned int n_dofs = dof_indices.size(); fe->reinit (elem); Ke.resize (n_dofs, n_dofs); Fe.resize (n_dofs); for (unsigned int qp=0; qp<qrule.n_points(); qp++) { for (unsigned int dof_i=0; dof_i<n_dofs; dof_i++) { for (unsigned int dof_j=0; dof_j<n_dofs; dof_j++) { Ke(dof_i, dof_j) += JxW[qp] * (dphi[dof_j][qp]* dphi[dof_i][qp]); } Fe(dof_i) += JxW[qp] * phi[dof_i][qp]; } } A_matrix->add_matrix (Ke, dof_indices); F_vector->add_vector (Fe, dof_indices); } A_matrix->close(); F_vector->close(); }
void transpose(const DenseMatrix &A, DenseMatrix &Atranspose) { Atranspose.resize(A.n, A.m); for(int j=0; j<A.n; ++j) for(int i=0; i<A.m; ++i){ Atranspose(j,i)=A(i,j); } }
void DenseMatrix<T>::get_transpose (DenseMatrix<T>& dest) const { dest.resize(this->n(), this->m()); for (unsigned int i=0; i<dest.m(); i++) for (unsigned int j=0; j<dest.n(); j++) dest(i,j) = (*this)(j,i); }
void DenseMatrix<T>::get_principal_submatrix (unsigned int sub_m, unsigned int sub_n, DenseMatrix<T>& dest) const { libmesh_assert( (sub_m <= this->m()) && (sub_n <= this->n()) ); dest.resize(sub_m, sub_n); for(unsigned int i=0; i<sub_m; i++) for(unsigned int j=0; j<sub_n; j++) dest(i,j) = (*this)(i,j); }
void dataLoad(std::istream & stream, DenseMatrix<Real> & v, void * /*context*/) { unsigned int nr = 0, nc = 0; stream.read((char *) &nr, sizeof(nr)); stream.read((char *) &nc, sizeof(nc)); v.resize(nr,nc); for (unsigned int i = 0; i < v.m(); i++) for (unsigned int j = 0; j < v.n(); j++) { Real r = 0; stream.read((char *) &r, sizeof(r)); v(i, j) = r; } }
void NonlinearNeoHookeCurrentConfig::get_linearized_stiffness(DenseMatrix<Real> & stiffness, unsigned int & i, unsigned int & j) { stiffness.resize(3, 3); double G_IK = (sigma * dphi[i][current_qp]) * dphi[j][current_qp]; stiffness(0, 0) += G_IK; stiffness(1, 1) += G_IK; stiffness(2, 2) += G_IK; B_L.resize(3, 6); this->build_b_0_mat(i, B_L); B_K.resize(3, 6); this->build_b_0_mat(j, B_K); B_L.right_multiply(C_mat); B_L.right_multiply_transpose(B_K); B_L *= 1/F.det(); stiffness += B_L; }
void testSVD() { DenseMatrix<Number> U, VT; DenseVector<Real> sigma; DenseMatrix<Number> A; A.resize(3, 2); A(0,0) = 1.0; A(0,1) = 2.0; A(1,0) = 3.0; A(1,1) = 4.0; A(2,0) = 5.0; A(2,1) = 6.0; A.svd(sigma, U, VT); // Solution for this case is (verified with numpy) DenseMatrix<Number> true_U(3,2), true_VT(2,2); DenseVector<Real> true_sigma(2); true_U(0,0) = -2.298476964000715e-01; true_U(0,1) = 8.834610176985250e-01; true_U(1,0) = -5.247448187602936e-01; true_U(1,1) = 2.407824921325463e-01; true_U(2,0) = -8.196419411205157e-01; true_U(2,1) = -4.018960334334318e-01; true_VT(0,0) = -6.196294838293402e-01; true_VT(0,1) = -7.848944532670524e-01; true_VT(1,0) = -7.848944532670524e-01; true_VT(1,1) = 6.196294838293400e-01; true_sigma(0) = 9.525518091565109e+00; true_sigma(1) = 5.143005806586446e-01; for (unsigned i=0; i<U.m(); ++i) for (unsigned j=0; j<U.n(); ++j) CPPUNIT_ASSERT_DOUBLES_EQUAL( libmesh_real(U(i,j)), libmesh_real(true_U(i,j)), TOLERANCE*TOLERANCE); for (unsigned i=0; i<VT.m(); ++i) for (unsigned j=0; j<VT.n(); ++j) CPPUNIT_ASSERT_DOUBLES_EQUAL( libmesh_real(VT(i,j)), libmesh_real(true_VT(i,j)), TOLERANCE*TOLERANCE); for (unsigned i=0; i<sigma.size(); ++i) CPPUNIT_ASSERT_DOUBLES_EQUAL(sigma(i), true_sigma(i), TOLERANCE*TOLERANCE); }
// This function defines the assembly routine which // will be called at each time step. It is responsible // for computing the proper matrix entries for the // element stiffness matrices and right-hand sides. void assemble_cd (EquationSystems& es, const std::string& system_name) { #ifdef LIBMESH_ENABLE_AMR // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Convection-Diffusion"); // 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(); // Get a reference to the Convection-Diffusion system object. TransientLinearImplicitSystem & system = es.get_system<TransientLinearImplicitSystem> ("Convection-Diffusion"); // Get the Finite Element type for the first (and only) // variable in the system. FEType fe_type = system.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)); AutoPtr<FEBase> fe_face (FEBase::build(dim, fe_type)); // A Gauss quadrature rule for numerical integration. // Let the \p FEType object decide what order rule is appropriate. QGauss qrule (dim, fe_type.default_quadrature_order()); QGauss qface (dim-1, fe_type.default_quadrature_order()); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (&qrule); 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 will start // with the element Jacobian * quadrature weight at each integration point. const std::vector<Real>& JxW = fe->get_JxW(); const std::vector<Real>& JxW_face = fe_face->get_JxW(); // The element shape functions evaluated at the quadrature points. const std::vector<std::vector<Real> >& phi = fe->get_phi(); const std::vector<std::vector<Real> >& psi = fe_face->get_phi(); // The element shape function gradients evaluated at the quadrature // points. const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi(); // The XY locations of the quadrature points used for face integration const std::vector<Point>& qface_points = fe_face->get_xyz(); // 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(); // Define data structures to contain the element matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke" and "Fe". DenseMatrix<Number> Ke; DenseVector<Number> Fe; // 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; // Here we extract the velocity & parameters that we put in the // EquationSystems object. const RealVectorValue velocity = es.parameters.get<RealVectorValue> ("velocity"); const Real diffusivity = es.parameters.get<Real> ("diffusivity"); const Real dt = es.parameters.get<Real> ("dt"); // Now we will loop over all the elements in the mesh that // live on the local processor. We will compute the element // matrix and right-hand-side contribution. Since the mesh // will be refined we want to only consider the ACTIVE elements, // hence we use a variant of the \p active_elem_iterator. 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 matrix and right-hand side 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()); Fe.resize (dof_indices.size()); // Now we will build the element matrix and right-hand-side. // Constructing the RHS requires the solution and its // gradient from the previous timestep. This myst be // calculated at each quadrature point by summing the // solution degree-of-freedom values by the appropriate // weight functions. for (unsigned int qp=0; qp<qrule.n_points(); qp++) { // Values to hold the old solution & its gradient. Number u_old = 0.; Gradient grad_u_old; // Compute the old solution & its gradient. for (unsigned int l=0; l<phi.size(); l++) { u_old += phi[l][qp]*system.old_solution (dof_indices[l]); // This will work, // grad_u_old += dphi[l][qp]*system.old_solution (dof_indices[l]); // but we can do it without creating a temporary like this: grad_u_old.add_scaled (dphi[l][qp],system.old_solution (dof_indices[l])); } // Now compute the element matrix and RHS contributions. for (unsigned int i=0; i<phi.size(); i++) { // The RHS contribution Fe(i) += JxW[qp]*( // Mass matrix term u_old*phi[i][qp] + -.5*dt*( // Convection term // (grad_u_old may be complex, so the // order here is important!) (grad_u_old*velocity)*phi[i][qp] + // Diffusion term diffusivity*(grad_u_old*dphi[i][qp])) ); for (unsigned int j=0; j<phi.size(); j++) { // The matrix contribution Ke(i,j) += JxW[qp]*( // Mass-matrix phi[i][qp]*phi[j][qp] + .5*dt*( // Convection term (velocity*dphi[j][qp])*phi[i][qp] + // Diffusion term diffusivity*(dphi[i][qp]*dphi[j][qp])) ); } } } // At this point the interior element integration has // been completed. However, we have not yet addressed // boundary conditions. For this example we will only // consider simple Dirichlet boundary conditions imposed // via the penalty method. // // 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. { // The penalty value. const Real penalty = 1.e10; // 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 s=0; s<elem->n_sides(); s++) if (elem->neighbor(s) == NULL) { fe_face->reinit(elem,s); for (unsigned int qp=0; qp<qface.n_points(); qp++) { const Number value = exact_solution (qface_points[qp](0), qface_points[qp](1), system.time); // RHS contribution for (unsigned int i=0; i<psi.size(); i++) Fe(i) += penalty*JxW_face[qp]*value*psi[i][qp]; // Matrix contribution for (unsigned int i=0; i<psi.size(); i++) for (unsigned int j=0; j<psi.size(); j++) Ke(i,j) += penalty*JxW_face[qp]*psi[i][qp]*psi[j][qp]; } } } // We have now built the element matrix and RHS vector in terms // of the element degrees of freedom. However, it is possible // that some of the element DOFs are constrained to enforce // solution continuity, i.e. they are not really "free". We need // to constrain those DOFs in terms of non-constrained DOFs to // ensure a continuous solution. The // \p DofMap::constrain_element_matrix_and_vector() method does // just that. dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // The element matrix and right-hand-side are now built // for this element. Add them to the global matrix and // right-hand-side vector. The \p SparseMatrix::add_matrix() // and \p NumericVector::add_vector() members do this for us. system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); } // Finished computing the sytem matrix and right-hand side. #endif // #ifdef LIBMESH_ENABLE_AMR }
// We now define the matrix assembly function for the // Poisson system. We need to first compute element // matrices and right-hand sides, and then take into // account the boundary conditions, which will be handled // via a penalty method. void assemble_poisson(EquationSystems& es, const std::string& system_name) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert (system_name == "Poisson"); // 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(); // Get a reference to the LinearImplicitSystem we are solving LinearImplicitSystem& system = es.get_system<LinearImplicitSystem> ("Poisson"); // A reference to the DofMap object for this system. The DofMap // object handles the index translation from node and element numbers // to degree of freedom numbers. We will talk more about the 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 // FEBase::build() member dynamically creates memory we will // store the object as an AutoPtr<FEBase>. This can be thought // of as a pointer that will clean up after itself. Example 4 // describes some advantages of AutoPtr's in the context of // quadrature rules. 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 finite 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. // // The element Jacobian * quadrature weight at each integration point. const std::vector<Real>& JxW = fe->get_JxW(); // The physical XY locations of the quadrature points on the element. // These might be useful for evaluating spatially varying material // properties at the quadrature points. const std::vector<Point>& q_point = fe->get_xyz(); // 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 element matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke" and "Fe". These datatypes are templated on // Number, which allows the same code to work for real // or complex numbers. DenseMatrix<Number> Ke; DenseVector<Number> Fe; // 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<unsigned int> dof_indices; // Now we will loop over all the elements in the mesh. // We will compute the element matrix and right-hand-side // contribution. // // Element iterators are a nice way to iterate through all the // elements, or all the elements that have some property. The // iterator el will iterate from the first to the last element on // the local processor. The iterator end_el tells us when to stop. // It is smart to make this one const so that we don't accidentally // mess it up! In case users later modify this program to include // refinement, we will be safe and will only consider the active // elements; hence we use a variant of the \p active_elem_iterator. MeshBase::const_element_iterator el = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end(); // Loop over the elements. Note that ++el is preferred to // el++ since the latter requires an unnecessary temporary // object. 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 matrix and right-hand side 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). // The DenseMatrix::resize() and the DenseVector::resize() // members will automatically zero out the matrix and vector. Ke.resize (dof_indices.size(), dof_indices.size()); Fe.resize (dof_indices.size()); // Now loop over the quadrature points. This handles // the numeric integration. for (unsigned int qp=0; qp<qrule.n_points(); qp++) { // Now we will build the element matrix. This involves // a double loop to integrate the test funcions (i) against // the trial functions (j). for (unsigned int i=0; i<phi.size(); i++) for (unsigned int j=0; j<phi.size(); j++) { Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]); } // This is the end of the matrix summation loop // Now we build the element right-hand-side contribution. // This involves a single loop in which we integrate the // "forcing function" in the PDE against the test functions. { const Real x = q_point[qp](0); const Real y = q_point[qp](1); const Real eps = 1.e-3; // "fxy" is the forcing function for the Poisson equation. // In this case we set fxy to be a finite difference // Laplacian approximation to the (known) exact solution. // // We will use the second-order accurate FD Laplacian // approximation, which in 2D is // // u_xx + u_yy = (u(i,j-1) + u(i,j+1) + // u(i-1,j) + u(i+1,j) + // -4*u(i,j))/h^2 // // Since the value of the forcing function depends only // on the location of the quadrature point (q_point[qp]) // we will compute it here, outside of the i-loop const Real fxy = -(exact_solution(x,y-eps) + exact_solution(x,y+eps) + exact_solution(x-eps,y) + exact_solution(x+eps,y) - 4.*exact_solution(x,y))/eps/eps; for (unsigned int i=0; i<phi.size(); i++) Fe(i) += JxW[qp]*fxy*phi[i][qp]; } } // We have now reached the end of the RHS summation, // and the end of quadrature point loop, so // the interior element integration has // been completed. However, we have not yet addressed // boundary conditions. For this example we will only // consider simple Dirichlet boundary conditions. // // There are several ways Dirichlet boundary conditions // can be imposed. A simple approach, which works for // interpolary bases like the standard Lagrange polynomials, // is to assign function values to the // degrees of freedom living on the domain boundary. This // works well for interpolary bases, but is more difficult // when non-interpolary (e.g Legendre or Hierarchic) bases // are used. // // Dirichlet boundary conditions can also be imposed with a // "penalty" method. In this case essentially the L2 projection // of the boundary values are added to the matrix. The // projection is multiplied by some large factor so that, in // floating point arithmetic, the existing (smaller) entries // in the matrix and right-hand-side are effectively ignored. // // This amounts to adding a term of the form (in latex notation) // // \frac{1}{\epsilon} \int_{\delta \Omega} \phi_i \phi_j = \frac{1}{\epsilon} \int_{\delta \Omega} u \phi_i // // where // // \frac{1}{\epsilon} is the penalty parameter, defined such that \epsilon << 1 { // The following loop is 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(); // The XYZ locations (in physical space) of the // quadrature points on the face. This is where // we will interpolate the boundary value function. const std::vector<Point >& qface_point = fe_face->get_xyz(); // 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++) { // The location on the boundary of the current // face quadrature point. const Real xf = qface_point[qp](0); const Real yf = qface_point[qp](1); // The penalty value. \frac{1}{\epsilon} // in the discussion above. const Real penalty = 1.e10; // The boundary value. const Real value = exact_solution(xf, yf); // Matrix contribution of the L2 projection. for (unsigned int i=0; i<phi_face.size(); i++) for (unsigned int j=0; j<phi_face.size(); j++) Ke(i,j) += JxW_face[qp]*penalty*phi_face[i][qp]*phi_face[j][qp]; // Right-hand-side contribution of the L2 // projection. for (unsigned int i=0; i<phi_face.size(); i++) Fe(i) += JxW_face[qp]*penalty*value*phi_face[i][qp]; } } } // We have now finished the quadrature point loop, // and have therefore applied all the boundary conditions. // If this assembly program were to be used on an adaptive mesh, // we would have to apply any hanging node constraint equations dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // The element matrix and right-hand-side are now built // for this element. Add them to the global matrix and // right-hand-side vector. The SparseMatrix::add_matrix() // and NumericVector::add_vector() members do this for us. system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); } // All done! }
void assemble_matrices(EquationSystems & es, const std::string & libmesh_dbg_var(system_name)) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Eigensystem"); #ifdef LIBMESH_HAVE_SLEPC // 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(); // Get a reference to our system. EigenSystem & eigen_system = es.get_system<EigenSystem> ("Eigensystem"); // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. FEType fe_type = eigen_system.get_dof_map().variable_type(0); // A reference to the two system matrices SparseMatrix<Number> & matrix_A = *eigen_system.matrix_A; SparseMatrix<Number> & matrix_B = *eigen_system.matrix_B; // Build a Finite Element object of the specified type. Since the // FEBase::build() member dynamically creates memory we will // store the object as a std::unique_ptr<FEBase>. This can be thought // of as a pointer that will clean up after itself. std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type)); // A Gauss quadrature rule for numerical integration. // Use the default quadrature order. QGauss qrule (dim, fe_type.default_quadrature_order()); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (&qrule); // 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(); // A reference to the DofMap object for this system. The DofMap // object handles the index translation from node and element numbers // to degree of freedom numbers. const DofMap & dof_map = eigen_system.get_dof_map(); // The element mass and stiffness matrices. DenseMatrix<Number> Me; 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 elements in the mesh that // live on the local processor. We will compute the element // matrix and right-hand-side contribution. In case users // later modify this program to include refinement, we will // be safe and will only consider the active elements; // hence we use a variant of the active_elem_iterator. for (const auto & elem : mesh.active_local_element_ptr_range()) { // 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 matrices 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). const unsigned int n_dofs = cast_int<unsigned int>(dof_indices.size()); Ke.resize (n_dofs, n_dofs); Me.resize (n_dofs, n_dofs); // Now loop over the quadrature points. This handles // the numeric integration. // // We will build the element matrix. This involves // a double loop to integrate the test functions (i) against // the trial functions (j). for (unsigned int qp=0; qp<qrule.n_points(); qp++) for (unsigned int i=0; i<n_dofs; i++) for (unsigned int j=0; j<n_dofs; j++) { Me(i,j) += JxW[qp]*phi[i][qp]*phi[j][qp]; Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]); } // The calls to constrain_element_matrix below have no effect in // the current example. However, if users modify this example to // include hanging nodes due to mesh refinement, for example, // then it is essential to call constrain_element_matrix. // As a result we include constrain_element_matrix here to // ensure this example is ready to be used with hanging nodes. // (Note that constrained rows/cols will be eliminated from // the eigenproblem by the CondensedEigenSystem.) dof_map.constrain_element_matrix(Ke, dof_indices, false); dof_map.constrain_element_matrix(Me, dof_indices, false); // Finally, simply add the element contribution to the // overall matrices A and B. matrix_A.add_matrix (Ke, dof_indices); matrix_B.add_matrix (Me, dof_indices); } // end of element loop #else // Avoid compiler warnings libmesh_ignore(es); #endif // LIBMESH_HAVE_SLEPC }
void assemble_matrices(EquationSystems& es, const std::string& system_name) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Eigensystem"); #ifdef LIBMESH_HAVE_SLEPC // 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(); // Get a reference to our system. EigenSystem & eigen_system = es.get_system<EigenSystem> (system_name); // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. FEType fe_type = eigen_system.get_dof_map().variable_type(0); // A reference to the two system matrices SparseMatrix<Number>& matrix_A = *eigen_system.matrix_A; SparseMatrix<Number>& matrix_B = *eigen_system.matrix_B; // 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 Gauss quadrature rule for numerical integration. // Use the default quadrature order. QGauss qrule (dim, fe_type.default_quadrature_order()); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (&qrule); // 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(); // 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. const DofMap& dof_map = eigen_system.get_dof_map(); // The element mass and stiffness matrices. DenseMatrix<Number> Me; 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<unsigned int> dof_indices; // Now we will loop over all the elements in the mesh that // live on the local processor. We will compute the element // matrix and right-hand-side contribution. In case users // later modify this program to include refinement, we will // be safe and will only consider the active elements; // hence we use a variant of the \p active_elem_iterator. 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 matrices 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()); Me.resize (dof_indices.size(), dof_indices.size()); // Now loop over the quadrature points. This handles // the numeric integration. // // We will build the element matrix. This involves // a double loop to integrate the test funcions (i) against // the trial functions (j). for (unsigned int qp=0; qp<qrule.n_points(); qp++) for (unsigned int i=0; i<phi.size(); i++) for (unsigned int j=0; j<phi.size(); j++) { Me(i,j) += JxW[qp]*phi[i][qp]*phi[j][qp]; Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]); } // On an unrefined mesh, constrain_element_matrix does // nothing. If this assembly function is ever repurposed to // run on a refined mesh, getting the hanging node constraints // right will be important. Note that, even with // asymmetric_constraint_rows = false, the constrained dof // diagonals still exist in the matrix, with diagonal entries // that are there to ensure non-singular matrices for linear // solves but which would generate positive non-physical // eigenvalues for eigensolves. // dof_map.constrain_element_matrix(Ke, dof_indices, false); // dof_map.constrain_element_matrix(Me, dof_indices, false); // Finally, simply add the element contribution to the // overall matrices A and B. matrix_A.add_matrix (Ke, dof_indices); matrix_B.add_matrix (Me, dof_indices); } // end of element loop #endif // LIBMESH_HAVE_SLEPC /** * All done! */ return; }
void assemble_stokes (EquationSystems& es, const std::string& system_name) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Stokes"); // 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(); // Get a reference to the Convection-Diffusion system object. LinearImplicitSystem & system = es.get_system<LinearImplicitSystem> ("Stokes"); // Numeric ids corresponding to each variable in the system const unsigned int u_var = system.variable_number ("u"); const unsigned int v_var = system.variable_number ("v"); const unsigned int p_var = system.variable_number ("p"); // Get the Finite Element type for "u". Note this will be // the same as the type for "v". FEType fe_vel_type = system.variable_type(u_var); // Get the Finite Element type for "p". FEType fe_pres_type = system.variable_type(p_var); // Build a Finite Element object of the specified type for // the velocity variables. UniquePtr<FEBase> fe_vel (FEBase::build(dim, fe_vel_type)); // Build a Finite Element object of the specified type for // the pressure variables. UniquePtr<FEBase> fe_pres (FEBase::build(dim, fe_pres_type)); // A Gauss quadrature rule for numerical integration. // Let the \p FEType object decide what order rule is appropriate. QGauss qrule (dim, fe_vel_type.default_quadrature_order()); // Tell the finite element objects to use our quadrature rule. fe_vel->attach_quadrature_rule (&qrule); fe_pres->attach_quadrature_rule (&qrule); // Here we define some references to cell-specific data that // will be used to assemble the linear system. // // The element Jacobian * quadrature weight at each integration point. const std::vector<Real>& JxW = fe_vel->get_JxW(); // The element shape function gradients for the velocity // variables evaluated at the quadrature points. const std::vector<std::vector<RealGradient> >& dphi = fe_vel->get_dphi(); // The element shape functions for the pressure variable // evaluated at the quadrature points. const std::vector<std::vector<Real> >& psi = fe_pres->get_phi(); // 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(); // Define data structures to contain the element matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke" and "Fe". DenseMatrix<Number> Ke; DenseVector<Number> Fe; DenseSubMatrix<Number> Kuu(Ke), Kuv(Ke), Kup(Ke), Kvu(Ke), Kvv(Ke), Kvp(Ke), Kpu(Ke), Kpv(Ke), Kpp(Ke); DenseSubVector<Number> Fu(Fe), Fv(Fe), Fp(Fe); // 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; std::vector<dof_id_type> dof_indices_u; std::vector<dof_id_type> dof_indices_v; std::vector<dof_id_type> dof_indices_p; // Now we will loop over all the elements in the mesh that // live on the local processor. We will compute the element // matrix and right-hand-side contribution. In case users later // modify this program to include refinement, we will be safe and // will only consider the active elements; hence we use a variant of // the \p active_elem_iterator. 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); dof_map.dof_indices (elem, dof_indices_u, u_var); dof_map.dof_indices (elem, dof_indices_v, v_var); dof_map.dof_indices (elem, dof_indices_p, p_var); const unsigned int n_dofs = dof_indices.size(); const unsigned int n_u_dofs = dof_indices_u.size(); const unsigned int n_v_dofs = dof_indices_v.size(); const unsigned int n_p_dofs = dof_indices_p.size(); // 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_vel->reinit (elem); fe_pres->reinit (elem); // Zero the element matrix and right-hand side 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 (n_dofs, n_dofs); Fe.resize (n_dofs); // Reposition the submatrices... The idea is this: // // - - - - // | Kuu Kuv Kup | | Fu | // Ke = | Kvu Kvv Kvp |; Fe = | Fv | // | Kpu Kpv Kpp | | Fp | // - - - - // // The \p DenseSubMatrix.repostition () member takes the // (row_offset, column_offset, row_size, column_size). // // Similarly, the \p DenseSubVector.reposition () member // takes the (row_offset, row_size) Kuu.reposition (u_var*n_u_dofs, u_var*n_u_dofs, n_u_dofs, n_u_dofs); Kuv.reposition (u_var*n_u_dofs, v_var*n_u_dofs, n_u_dofs, n_v_dofs); Kup.reposition (u_var*n_u_dofs, p_var*n_u_dofs, n_u_dofs, n_p_dofs); Kvu.reposition (v_var*n_v_dofs, u_var*n_v_dofs, n_v_dofs, n_u_dofs); Kvv.reposition (v_var*n_v_dofs, v_var*n_v_dofs, n_v_dofs, n_v_dofs); Kvp.reposition (v_var*n_v_dofs, p_var*n_v_dofs, n_v_dofs, n_p_dofs); Kpu.reposition (p_var*n_u_dofs, u_var*n_u_dofs, n_p_dofs, n_u_dofs); Kpv.reposition (p_var*n_u_dofs, v_var*n_u_dofs, n_p_dofs, n_v_dofs); Kpp.reposition (p_var*n_u_dofs, p_var*n_u_dofs, n_p_dofs, n_p_dofs); Fu.reposition (u_var*n_u_dofs, n_u_dofs); Fv.reposition (v_var*n_u_dofs, n_v_dofs); Fp.reposition (p_var*n_u_dofs, n_p_dofs); // Now we will build the element matrix. for (unsigned int qp=0; qp<qrule.n_points(); qp++) { // Assemble the u-velocity row // uu coupling for (unsigned int i=0; i<n_u_dofs; i++) for (unsigned int j=0; j<n_u_dofs; j++) Kuu(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]); // up coupling for (unsigned int i=0; i<n_u_dofs; i++) for (unsigned int j=0; j<n_p_dofs; j++) Kup(i,j) += -JxW[qp]*psi[j][qp]*dphi[i][qp](0); // Assemble the v-velocity row // vv coupling for (unsigned int i=0; i<n_v_dofs; i++) for (unsigned int j=0; j<n_v_dofs; j++) Kvv(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]); // vp coupling for (unsigned int i=0; i<n_v_dofs; i++) for (unsigned int j=0; j<n_p_dofs; j++) Kvp(i,j) += -JxW[qp]*psi[j][qp]*dphi[i][qp](1); // Assemble the pressure row // pu coupling for (unsigned int i=0; i<n_p_dofs; i++) for (unsigned int j=0; j<n_u_dofs; j++) Kpu(i,j) += -JxW[qp]*psi[i][qp]*dphi[j][qp](0); // pv coupling for (unsigned int i=0; i<n_p_dofs; i++) for (unsigned int j=0; j<n_v_dofs; j++) Kpv(i,j) += -JxW[qp]*psi[i][qp]*dphi[j][qp](1); } // end of the quadrature point qp-loop // At this point the interior element integration has // been completed. However, we have not yet addressed // boundary conditions. For this example we will only // consider simple Dirichlet boundary conditions imposed // via the penalty method. The penalty method used here // is equivalent (for Lagrange basis functions) to lumping // the matrix resulting from the L2 projection penalty // approach introduced in example 3. { // 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 s=0; s<elem->n_sides(); s++) if (elem->neighbor(s) == NULL) { UniquePtr<Elem> side (elem->build_side(s)); // Loop over the nodes on the side. for (unsigned int ns=0; ns<side->n_nodes(); ns++) { // The location on the boundary of the current // node. // const Real xf = side->point(ns)(0); const Real yf = side->point(ns)(1); // The penalty value. \f$ \frac{1}{\epsilon \f$ const Real penalty = 1.e10; // The boundary values. // Set u = 1 on the top boundary, 0 everywhere else const Real u_value = (yf > .99) ? 1. : 0.; // Set v = 0 everywhere const Real v_value = 0.; // Find the node on the element matching this node on // the side. That defined where in the element matrix // the boundary condition will be applied. for (unsigned int n=0; n<elem->n_nodes(); n++) if (elem->node(n) == side->node(ns)) { // Matrix contribution. Kuu(n,n) += penalty; Kvv(n,n) += penalty; // Right-hand-side contribution. Fu(n) += penalty*u_value; Fv(n) += penalty*v_value; } } // end face node loop } // end if (elem->neighbor(side) == NULL) } // end boundary condition section // If this assembly program were to be used on an adaptive mesh, // we would have to apply any hanging node constraint equations. dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // The element matrix and right-hand-side are now built // for this element. Add them to the global matrix and // right-hand-side vector. The \p NumericMatrix::add_matrix() // and \p NumericVector::add_vector() members do this for us. system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); } // end of element loop // That's it. return; }
void MultiAppProjectionTransfer::assembleL2To(EquationSystems & es, const std::string & system_name) { unsigned int app = es.parameters.get<unsigned int>("app"); FEProblem & from_problem = *_multi_app->problem(); EquationSystems & from_es = from_problem.es(); MooseVariable & from_var = from_problem.getVariable(0, _from_var_name); System & from_sys = from_var.sys().system(); unsigned int from_var_num = from_sys.variable_number(from_var.name()); NumericVector<Number> * serialized_from_solution = NumericVector<Number>::build(from_sys.comm()).release(); serialized_from_solution->init(from_sys.n_dofs(), false, SERIAL); // Need to pull down a full copy of this vector on every processor so we can get values in parallel from_sys.solution->localize(*serialized_from_solution); MeshFunction from_func(from_es, *serialized_from_solution, from_sys.get_dof_map(), from_var_num); from_func.init(Trees::ELEMENTS); from_func.enable_out_of_mesh_mode(0.); const MeshBase& mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>(system_name); FEType fe_type = system.variable_type(0); AutoPtr<FEBase> fe(FEBase::build(dim, fe_type)); QGauss qrule(dim, fe_type.default_quadrature_order()); fe->attach_quadrature_rule(&qrule); const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<std::vector<Real> > & phi = fe->get_phi(); const std::vector<Point> & xyz = fe->get_xyz(); const DofMap& dof_map = system.get_dof_map(); DenseMatrix<Number> Ke; DenseVector<Number> Fe; std::vector<dof_id_type> dof_indices; 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) { const Elem* elem = *el; fe->reinit (elem); dof_map.dof_indices (elem, dof_indices); Ke.resize (dof_indices.size(), dof_indices.size()); Fe.resize (dof_indices.size()); for (unsigned int qp = 0; qp < qrule.n_points(); qp++) { Point qpt = xyz[qp]; Point pt = qpt + _multi_app->position(app); Real f = from_func(pt); // Now compute the element matrix and RHS contributions. for (unsigned int i=0; i<phi.size(); i++) { // RHS Fe(i) += JxW[qp] * (f * phi[i][qp]); if (_compute_matrix) for (unsigned int j = 0; j < phi.size(); j++) { // The matrix contribution Ke(i,j) += JxW[qp] * (phi[i][qp] * phi[j][qp]); } } dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices); if (_compute_matrix) system.matrix->add_matrix(Ke, dof_indices); system.rhs->add_vector(Fe, dof_indices); } } }
void MultiAppProjectionTransfer::assembleL2(EquationSystems & es, const std::string & system_name) { // Get the system and mesh from the input arguments. LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>(system_name); MeshBase & to_mesh = es.get_mesh(); // Get the meshfunction evaluations and the map that was stashed in the es. std::vector<Real> & final_evals = * es.parameters.get<std::vector<Real>*>("final_evals"); std::map<unsigned int, unsigned int> & element_map = * es.parameters.get<std::map<unsigned int, unsigned int>*>("element_map"); // Setup system vectors and matrices. FEType fe_type = system.variable_type(0); std::unique_ptr<FEBase> fe(FEBase::build(to_mesh.mesh_dimension(), fe_type)); QGauss qrule(to_mesh.mesh_dimension(), fe_type.default_quadrature_order()); fe->attach_quadrature_rule(&qrule); const DofMap& dof_map = system.get_dof_map(); DenseMatrix<Number> Ke; DenseVector<Number> Fe; std::vector<dof_id_type> dof_indices; const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<std::vector<Real> > & phi = fe->get_phi(); const MeshBase::const_element_iterator end_el = to_mesh.active_local_elements_end(); for (MeshBase::const_element_iterator el = to_mesh.active_local_elements_begin(); el != end_el; ++el) { const Elem* elem = *el; fe->reinit (elem); dof_map.dof_indices (elem, dof_indices); Ke.resize (dof_indices.size(), dof_indices.size()); Fe.resize (dof_indices.size()); for (unsigned int qp = 0; qp < qrule.n_points(); qp++) { Real meshfun_eval = 0.; if (element_map.find(elem->id()) != element_map.end()) { // We have evaluations for this element. meshfun_eval = final_evals[element_map[elem->id()] + qp]; } // Now compute the element matrix and RHS contributions. for (unsigned int i=0; i<phi.size(); i++) { // RHS Fe(i) += JxW[qp] * (meshfun_eval * phi[i][qp]); if (_compute_matrix) for (unsigned int j = 0; j < phi.size(); j++) { // The matrix contribution Ke(i,j) += JxW[qp] * (phi[i][qp] * phi[j][qp]); } } dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices); if (_compute_matrix) system.matrix->add_matrix(Ke, dof_indices); system.rhs->add_vector(Fe, dof_indices); } } }
// We now define the matrix assembly function for the // Laplace system. We need to first compute element volume // matrices, and then take into account the boundary // conditions and the flux integrals, which will be handled // via an interior penalty method. void assemble_ellipticdg(EquationSystems& es, const std::string& system_name) { std::cout<<" assembling elliptic dg system... "; std::cout.flush(); // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "EllipticDG"); // 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(); // Get a reference to the LinearImplicitSystem we are solving LinearImplicitSystem & ellipticdg_system = es.get_system<LinearImplicitSystem> ("EllipticDG"); // Get some parameters that we need during assembly const Real penalty = es.parameters.get<Real> ("penalty"); std::string refinement_type = es.parameters.get<std::string> ("refinement"); // 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 const DofMap & dof_map = ellipticdg_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 = ellipticdg_system.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)); AutoPtr<FEBase> fe_elem_face(FEBase::build(dim, fe_type)); AutoPtr<FEBase> fe_neighbor_face(FEBase::build(dim, fe_type)); // Quadrature rules for numerical integration. #ifdef QORDER QGauss qrule (dim, QORDER); #else QGauss qrule (dim, fe_type.default_quadrature_order()); #endif fe->attach_quadrature_rule (&qrule); #ifdef QORDER QGauss qface(dim-1, QORDER); #else QGauss qface(dim-1, fe_type.default_quadrature_order()); #endif // Tell the finite element object to use our quadrature rule. fe_elem_face->attach_quadrature_rule(&qface); fe_neighbor_face->attach_quadrature_rule(&qface); // Here we define some references to cell-specific data that // will be used to assemble the linear system. // Data for interior volume integrals const std::vector<Real>& JxW = fe->get_JxW(); const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi(); // Data for surface integrals on the element boundary const std::vector<std::vector<Real> >& phi_face = fe_elem_face->get_phi(); const std::vector<std::vector<RealGradient> >& dphi_face = fe_elem_face->get_dphi(); const std::vector<Real>& JxW_face = fe_elem_face->get_JxW(); const std::vector<Point>& qface_normals = fe_elem_face->get_normals(); const std::vector<Point>& qface_points = fe_elem_face->get_xyz(); // Data for surface integrals on the neighbor boundary const std::vector<std::vector<Real> >& phi_neighbor_face = fe_neighbor_face->get_phi(); const std::vector<std::vector<RealGradient> >& dphi_neighbor_face = fe_neighbor_face->get_dphi(); // Define data structures to contain the element interior matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke" and "Fe". DenseMatrix<Number> Ke; DenseVector<Number> Fe; // Data structures to contain the element and neighbor boundary matrix // contribution. This matrices will do the coupling beetwen the dofs of // the element and those of his neighbors. // Ken: matrix coupling elem and neighbor dofs DenseMatrix<Number> Kne; DenseMatrix<Number> Ken; DenseMatrix<Number> Kee; DenseMatrix<Number> Knn; // 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 elements in the mesh. We will // compute first the element interior matrix and right-hand-side contribution // and then the element and neighbors boundary matrix contributions. 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); const unsigned int n_dofs = dof_indices.size(); // 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 matrix and right-hand side before // summing them. We use the resize member here because // the number of degrees of freedom might have changed from // the last element. Ke.resize (n_dofs, n_dofs); Fe.resize (n_dofs); // Now we will build the element interior matrix. This involves // a double loop to integrate the test funcions (i) against // the trial functions (j). for (unsigned int qp=0; qp<qrule.n_points(); qp++) { for (unsigned int i=0; i<n_dofs; i++) { for (unsigned int j=0; j<n_dofs; j++) { Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]); } } } // Now we adress boundary conditions. // We consider Dirichlet bc imposed via the interior penalty method // 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) { // Pointer to the element face fe_elem_face->reinit(elem, side); AutoPtr<Elem> elem_side (elem->build_side(side)); // h elemet dimension to compute the interior penalty penalty parameter const unsigned int elem_b_order = static_cast<unsigned int> (fe_elem_face->get_order()); const double h_elem = elem->volume()/elem_side->volume() * 1./pow(elem_b_order, 2.); for (unsigned int qp=0; qp<qface.n_points(); qp++) { Number bc_value = exact_solution(qface_points[qp], es.parameters,"null","void"); for (unsigned int i=0; i<n_dofs; i++) { // Matrix contribution for (unsigned int j=0; j<n_dofs; j++) { Ke(i,j) += JxW_face[qp] * penalty/h_elem * phi_face[i][qp] * phi_face[j][qp]; // stability Ke(i,j) -= JxW_face[qp] * (phi_face[i][qp] * (dphi_face[j][qp]*qface_normals[qp]) + phi_face[j][qp] * (dphi_face[i][qp]*qface_normals[qp])); // consistency } // RHS contribution Fe(i) += JxW_face[qp] * bc_value * penalty/h_elem * phi_face[i][qp]; // stability Fe(i) -= JxW_face[qp] * dphi_face[i][qp] * (bc_value*qface_normals[qp]); // consistency } } } // If the element is not on a boundary of the domain // we loop over his neighbors to compute the element // and neighbor boundary matrix contributions else { // Store a pointer to the neighbor we are currently // working on. const Elem* neighbor = elem->neighbor(side); // Get the global id of the element and the neighbor const unsigned int elem_id = elem->id(); const unsigned int neighbor_id = neighbor->id(); // If the neighbor has the same h level and is active // perform integration only if our global id is bigger than our neighbor id. // We don't want to compute twice the same contributions. // If the neighbor has a different h level perform integration // only if the neighbor is at a lower level. if ((neighbor->active() && (neighbor->level() == elem->level()) && (elem_id < neighbor_id)) || (neighbor->level() < elem->level())) { // Pointer to the element side AutoPtr<Elem> elem_side (elem->build_side(side)); // h dimension to compute the interior penalty penalty parameter const unsigned int elem_b_order = static_cast<unsigned int>(fe_elem_face->get_order()); const unsigned int neighbor_b_order = static_cast<unsigned int>(fe_neighbor_face->get_order()); const double side_order = (elem_b_order + neighbor_b_order)/2.; const double h_elem = (elem->volume()/elem_side->volume()) * 1./pow(side_order,2.); // The quadrature point locations on the neighbor side std::vector<Point> qface_neighbor_point; // The quadrature point locations on the element side std::vector<Point > qface_point; // Reinitialize shape functions on the element side fe_elem_face->reinit(elem, side); // Get the physical locations of the element quadrature points qface_point = fe_elem_face->get_xyz(); // Find their locations on the neighbor unsigned int side_neighbor = neighbor->which_neighbor_am_i(elem); if (refinement_type == "p") fe_neighbor_face->side_map (neighbor, elem_side.get(), side_neighbor, qface.get_points(), qface_neighbor_point); else FEInterface::inverse_map (elem->dim(), fe->get_fe_type(), neighbor, qface_point, qface_neighbor_point); // Calculate the neighbor element shape functions at those locations fe_neighbor_face->reinit(neighbor, &qface_neighbor_point); // Get the degree of freedom indices for the // neighbor. These define where in the global // matrix this neighbor will contribute to. std::vector<dof_id_type> neighbor_dof_indices; dof_map.dof_indices (neighbor, neighbor_dof_indices); const unsigned int n_neighbor_dofs = neighbor_dof_indices.size(); // Zero the element and neighbor side matrix before // summing them. We use the resize member here because // the number of degrees of freedom might have changed from // the last element or neighbor. // Note that Kne and Ken are not square matrices if neighbor // and element have a different p level Kne.resize (n_neighbor_dofs, n_dofs); Ken.resize (n_dofs, n_neighbor_dofs); Kee.resize (n_dofs, n_dofs); Knn.resize (n_neighbor_dofs, n_neighbor_dofs); // Now we will build the element and neighbor // boundary matrices. This involves // a double loop to integrate the test funcions // (i) against the trial functions (j). for (unsigned int qp=0; qp<qface.n_points(); qp++) { // Kee Matrix. Integrate the element test function i // against the element test function j for (unsigned int i=0; i<n_dofs; i++) { for (unsigned int j=0; j<n_dofs; j++) { Kee(i,j) -= 0.5 * JxW_face[qp] * (phi_face[j][qp]*(qface_normals[qp]*dphi_face[i][qp]) + phi_face[i][qp]*(qface_normals[qp]*dphi_face[j][qp])); // consistency Kee(i,j) += JxW_face[qp] * penalty/h_elem * phi_face[j][qp]*phi_face[i][qp]; // stability } } // Knn Matrix. Integrate the neighbor test function i // against the neighbor test function j for (unsigned int i=0; i<n_neighbor_dofs; i++) { for (unsigned int j=0; j<n_neighbor_dofs; j++) { Knn(i,j) += 0.5 * JxW_face[qp] * (phi_neighbor_face[j][qp]*(qface_normals[qp]*dphi_neighbor_face[i][qp]) + phi_neighbor_face[i][qp]*(qface_normals[qp]*dphi_neighbor_face[j][qp])); // consistency Knn(i,j) += JxW_face[qp] * penalty/h_elem * phi_neighbor_face[j][qp]*phi_neighbor_face[i][qp]; // stability } } // Kne Matrix. Integrate the neighbor test function i // against the element test function j for (unsigned int i=0; i<n_neighbor_dofs; i++) { for (unsigned int j=0; j<n_dofs; j++) { Kne(i,j) += 0.5 * JxW_face[qp] * (phi_neighbor_face[i][qp]*(qface_normals[qp]*dphi_face[j][qp]) - phi_face[j][qp]*(qface_normals[qp]*dphi_neighbor_face[i][qp])); // consistency Kne(i,j) -= JxW_face[qp] * penalty/h_elem * phi_face[j][qp]*phi_neighbor_face[i][qp]; // stability } } // Ken Matrix. Integrate the element test function i // against the neighbor test function j for (unsigned int i=0; i<n_dofs; i++) { for (unsigned int j=0; j<n_neighbor_dofs; j++) { Ken(i,j) += 0.5 * JxW_face[qp] * (phi_neighbor_face[j][qp]*(qface_normals[qp]*dphi_face[i][qp]) - phi_face[i][qp]*(qface_normals[qp]*dphi_neighbor_face[j][qp])); // consistency Ken(i,j) -= JxW_face[qp] * penalty/h_elem * phi_face[i][qp]*phi_neighbor_face[j][qp]; // stability } } } // The element and neighbor boundary matrix are now built // for this side. Add them to the global matrix // The \p SparseMatrix::add_matrix() members do this for us. ellipticdg_system.matrix->add_matrix(Kne,neighbor_dof_indices,dof_indices); ellipticdg_system.matrix->add_matrix(Ken,dof_indices,neighbor_dof_indices); ellipticdg_system.matrix->add_matrix(Kee,dof_indices); ellipticdg_system.matrix->add_matrix(Knn,neighbor_dof_indices); } } } // The element interior matrix and right-hand-side are now built // for this element. Add them to the global matrix and // right-hand-side vector. The \p SparseMatrix::add_matrix() // and \p NumericVector::add_vector() members do this for us. ellipticdg_system.matrix->add_matrix(Ke, dof_indices); ellipticdg_system.rhs->add_vector(Fe, dof_indices); } std::cout << "done" << std::endl; }
void LinearElasticityWithContact::residual_and_jacobian (const NumericVector<Number> & soln, NumericVector<Number> * residual, SparseMatrix<Number> * jacobian, NonlinearImplicitSystem & /*sys*/) { EquationSystems & es = _sys.get_equation_systems(); const Real young_modulus = es.parameters.get<Real>("young_modulus"); const Real poisson_ratio = es.parameters.get<Real>("poisson_ratio"); const MeshBase & mesh = _sys.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); const unsigned int u_var = _sys.variable_number ("u"); DofMap & dof_map = _sys.get_dof_map(); FEType fe_type = dof_map.variable_type(u_var); UniquePtr<FEBase> fe (FEBase::build(dim, fe_type)); QGauss qrule (dim, fe_type.default_quadrature_order()); fe->attach_quadrature_rule (&qrule); UniquePtr<FEBase> fe_face (FEBase::build(dim, fe_type)); QGauss qface (dim-1, fe_type.default_quadrature_order()); fe_face->attach_quadrature_rule (&qface); UniquePtr<FEBase> fe_neighbor_face (FEBase::build(dim, fe_type)); fe_neighbor_face->attach_quadrature_rule (&qface); const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi(); if (jacobian) jacobian->zero(); if (residual) residual->zero(); // Do jacobian and residual assembly, including contact forces DenseVector<Number> Re; DenseSubVector<Number> Re_var[3] = {DenseSubVector<Number>(Re), DenseSubVector<Number>(Re), DenseSubVector<Number>(Re)}; DenseMatrix<Number> Ke; DenseSubMatrix<Number> Ke_var[3][3] = { {DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke)}, {DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke)}, {DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke)} }; std::vector<dof_id_type> dof_indices; std::vector< std::vector<dof_id_type> > dof_indices_var(3); 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) { const Elem * elem = *el; dof_map.dof_indices (elem, dof_indices); for (unsigned int var=0; var<3; var++) dof_map.dof_indices (elem, dof_indices_var[var], var); const unsigned int n_dofs = dof_indices.size(); const unsigned int n_var_dofs = dof_indices_var[0].size(); fe->reinit (elem); Re.resize (n_dofs); for (unsigned int var=0; var<3; var++) Re_var[var].reposition (var*n_var_dofs, n_var_dofs); Ke.resize (n_dofs, n_dofs); for (unsigned int var_i=0; var_i<3; var_i++) for (unsigned int var_j=0; var_j<3; var_j++) Ke_var[var_i][var_j].reposition (var_i*n_var_dofs, var_j*n_var_dofs, n_var_dofs, n_var_dofs); for (unsigned int qp=0; qp<qrule.n_points(); qp++) { // Row is variable u, v, or w column is x, y, or z DenseMatrix<Number> grad_u(3, 3); for (unsigned int var_i=0; var_i<3; var_i++) for (unsigned int var_j=0; var_j<3; var_j++) for (unsigned int j=0; j<n_var_dofs; j++) grad_u(var_i, var_j) += dphi[j][qp](var_j)*soln(dof_indices_var[var_i][j]); // - C_ijkl u_k,l v_i,j for (unsigned int dof_i=0; dof_i<n_var_dofs; dof_i++) for (unsigned int i=0; i<3; i++) for (unsigned int j=0; j<3; j++) for (unsigned int k=0; k<3; k++) for (unsigned int l=0; l<3; l++) Re_var[i](dof_i) -= JxW[qp] * elasticity_tensor(young_modulus, poisson_ratio, i, j, k, l) * grad_u(k,l) * dphi[dof_i][qp](j); // assemble \int_Omega C_ijkl u_k,l v_i,j \dx for (unsigned int dof_i=0; dof_i<n_var_dofs; dof_i++) for (unsigned int dof_j=0; dof_j<n_var_dofs; dof_j++) for (unsigned int i=0; i<3; i++) for (unsigned int j=0; j<3; j++) for (unsigned int k=0; k<3; k++) for (unsigned int l=0; l<3; l++) Ke_var[i][k](dof_i, dof_j) -= JxW[qp] * elasticity_tensor(young_modulus, poisson_ratio, i, j, k, l) * dphi[dof_j][qp](l) * dphi[dof_i][qp](j); } dof_map.constrain_element_matrix_and_vector (Ke, Re, dof_indices); if (jacobian) jacobian->add_matrix (Ke, dof_indices); if (residual) residual->add_vector (Re, dof_indices); } // Move a copy of the mesh based on the solution. This is used to get // the contact forces. UniquePtr<MeshBase> mesh_clone = mesh.clone(); move_mesh(*mesh_clone, soln); // Add contributions due to contact penalty forces. Only need to do this on // one processor. _lambda_plus_penalty_values.clear(); LIBMESH_BEST_UNORDERED_MAP<dof_id_type, dof_id_type>::iterator it = _augment_sparsity._contact_node_map.begin(); LIBMESH_BEST_UNORDERED_MAP<dof_id_type, dof_id_type>::iterator it_end = _augment_sparsity._contact_node_map.end(); for ( ; it != it_end; ++it) { dof_id_type lower_point_id = it->first; dof_id_type upper_point_id = it->second; Point upper_to_lower; { Point lower_node_moved = mesh_clone->point(lower_point_id); Point upper_node_moved = mesh_clone->point(upper_point_id); upper_to_lower = (lower_node_moved - upper_node_moved); } // Set the contact force direction. Usually this would be calculated // separately on each master node, but here we just set it to (0, 0, 1) // everywhere. Point contact_force_direction(0., 0., 1.); // gap_function > 0. means that contact has been detected // gap_function < 0. means that we have a gap // This sign convention matches Simo & Laursen (1992). Real gap_function = upper_to_lower * contact_force_direction; // We use the sign of lambda_plus_penalty to determine whether or // not we need to impose a contact force. Real lambda_plus_penalty = (_lambdas[upper_point_id] + gap_function * _contact_penalty); if (lambda_plus_penalty < 0.) lambda_plus_penalty = 0.; // Store lambda_plus_penalty, we'll need to use it later to update _lambdas _lambda_plus_penalty_values[upper_point_id] = lambda_plus_penalty; const Node & lower_node = mesh.node_ref(lower_point_id); const Node & upper_node = mesh.node_ref(upper_point_id); std::vector<dof_id_type> dof_indices_on_lower_node(3); std::vector<dof_id_type> dof_indices_on_upper_node(3); DenseVector<Number> lower_contact_force_vec(3); DenseVector<Number> upper_contact_force_vec(3); for (unsigned int var=0; var<3; var++) { dof_indices_on_lower_node[var] = lower_node.dof_number(_sys.number(), var, 0); lower_contact_force_vec(var) = -lambda_plus_penalty * contact_force_direction(var); dof_indices_on_upper_node[var] = upper_node.dof_number(_sys.number(), var, 0); upper_contact_force_vec(var) = lambda_plus_penalty * contact_force_direction(var); } if (lambda_plus_penalty > 0.) { if (residual && (_sys.comm().rank() == 0)) { residual->add_vector (lower_contact_force_vec, dof_indices_on_lower_node); residual->add_vector (upper_contact_force_vec, dof_indices_on_upper_node); } // Add the Jacobian terms due to the contact forces. The lambda contribution // is not relevant here because it doesn't depend on the solution. if (jacobian && (_sys.comm().rank() == 0)) for (unsigned int var=0; var<3; var++) for (unsigned int j=0; j<3; j++) { jacobian->add(dof_indices_on_lower_node[var], dof_indices_on_upper_node[j], _contact_penalty * contact_force_direction(j) * contact_force_direction(var)); jacobian->add(dof_indices_on_lower_node[var], dof_indices_on_lower_node[j], -_contact_penalty * contact_force_direction(j) * contact_force_direction(var)); jacobian->add(dof_indices_on_upper_node[var], dof_indices_on_lower_node[j], _contact_penalty * contact_force_direction(j) * contact_force_direction(var)); jacobian->add(dof_indices_on_upper_node[var], dof_indices_on_upper_node[j], -_contact_penalty * contact_force_direction(j) * contact_force_direction(var)); } } else { // We add zeros to the matrix even when lambda_plus_penalty = 0. // We do this because some linear algebra libraries (e.g. PETSc) // will condense out any unused entries from the sparsity pattern, // so adding these zeros in ensures that these entries are not // condensed out. if (jacobian && (_sys.comm().rank() == 0)) for (unsigned int var=0; var<3; var++) for (unsigned int j=0; j<3; j++) { jacobian->add(dof_indices_on_lower_node[var], dof_indices_on_upper_node[j], 0.); jacobian->add(dof_indices_on_lower_node[var], dof_indices_on_lower_node[j], 0.); jacobian->add(dof_indices_on_upper_node[var], dof_indices_on_lower_node[j], 0.); jacobian->add(dof_indices_on_upper_node[var], dof_indices_on_upper_node[j], 0.); } } } }
// We now define the matrix assembly function for the // Biharmonic system. We need to first compute element // matrices and right-hand sides, and then take into // account the boundary conditions, which will be handled // via a penalty method. void assemble_biharmonic(EquationSystems& es, const std::string& system_name) { #ifdef LIBMESH_ENABLE_AMR #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Biharmonic"); // Declare a performance log. Give it a descriptive // string to identify what part of the code we are // logging, since there may be many PerfLogs in an // application. PerfLog perf_log ("Matrix Assembly",false); // 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(); // Get a reference to the LinearImplicitSystem we are solving LinearImplicitSystem& system = es.get_system<LinearImplicitSystem>("Biharmonic"); // 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)); // Quadrature rule for numerical integration. // With 2D triangles, the Clough quadrature rule puts a Gaussian // quadrature rule on each of the 3 subelements AutoPtr<QBase> qrule(fe_type.default_quadrature_rule(dim)); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (qrule.get()); // Declare a special finite element object for // boundary integration. AutoPtr<FEBase> fe_face (FEBase::build(dim, fe_type)); // Boundary integration requires another quadraure rule, // with dimensionality one less than the dimensionality // of the element. // In 1D, the Clough and Gauss quadrature rules are identical. AutoPtr<QBase> qface(fe_type.default_quadrature_rule(dim-1)); // Tell the finte element object to use our // quadrature rule. fe_face->attach_quadrature_rule (qface.get()); // 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 physical XY locations of the quadrature points on the element. // These might be useful for evaluating spatially varying material // properties at the quadrature points. const std::vector<Point>& q_point = fe->get_xyz(); // The element shape functions evaluated at the quadrature points. const std::vector<std::vector<Real> >& phi = fe->get_phi(); // The element shape function second derivatives evaluated at the // quadrature points. Note that for the simple biharmonic, shape // function first derivatives are unnecessary. const std::vector<std::vector<RealTensor> >& d2phi = fe->get_d2phi(); // For efficiency we will compute shape function laplacians n times, // not n^2 std::vector<Real> shape_laplacian; // Define data structures to contain the element matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke" and "Fe". More detail is in example 3. DenseMatrix<Number> Ke; DenseVector<Number> Fe; // 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<unsigned int> dof_indices; // Now we will loop over all the elements in the mesh. We will // compute the element matrix and right-hand-side contribution. See // example 3 for a discussion of the element iterators. 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) { // Start logging the shape function initialization. // This is done through a simple function call with // the name of the event to log. perf_log.push("elem init"); // 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 matrix and right-hand side before // summing them. Ke.resize (dof_indices.size(), dof_indices.size()); Fe.resize (dof_indices.size()); // Make sure there is enough room in this cache shape_laplacian.resize(dof_indices.size()); // Stop logging the shape function initialization. // If you forget to stop logging an event the PerfLog // object will probably catch the error and abort. perf_log.pop("elem init"); // Now we will build the element matrix. This involves // a double loop to integrate laplacians of the test funcions // (i) against laplacians of the trial functions (j). // // This step is why we need the Clough-Tocher elements - // these C1 differentiable elements have square-integrable // second derivatives. // // Now start logging the element matrix computation perf_log.push ("Ke"); for (unsigned int qp=0; qp<qrule->n_points(); qp++) { for (unsigned int i=0; i<phi.size(); i++) { shape_laplacian[i] = d2phi[i][qp](0,0)+d2phi[i][qp](1,1); if (dim == 3) shape_laplacian[i] += d2phi[i][qp](2,2); } for (unsigned int i=0; i<phi.size(); i++) for (unsigned int j=0; j<phi.size(); j++) Ke(i,j) += JxW[qp]* shape_laplacian[i]*shape_laplacian[j]; } // Stop logging the matrix computation perf_log.pop ("Ke"); // At this point the interior element integration has // been completed. However, we have not yet addressed // boundary conditions. For this example we will only // consider simple Dirichlet boundary conditions imposed // via the penalty method. Note that this is a fourth-order // problem: Dirichlet boundary conditions include *both* // boundary values and boundary normal fluxes. { // Start logging the boundary condition computation perf_log.push ("BCs"); // The penalty values, for solution boundary trace and flux. const Real penalty = 1e10; const Real penalty2 = 1e10; // 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 s=0; s<elem->n_sides(); s++) if (elem->neighbor(s) == NULL) { // The value of the shape functions at the quadrature // points. const std::vector<std::vector<Real> >& phi_face = fe_face->get_phi(); // The value of the shape function derivatives at the // quadrature points. const std::vector<std::vector<RealGradient> >& dphi_face = fe_face->get_dphi(); // The Jacobian * Quadrature Weight at the quadrature // points on the face. const std::vector<Real>& JxW_face = fe_face->get_JxW(); // The XYZ locations (in physical space) of the // quadrature points on the face. This is where // we will interpolate the boundary value function. const std::vector<Point>& qface_point = fe_face->get_xyz(); const std::vector<Point>& face_normals = fe_face->get_normals(); // Compute the shape function values on the element // face. fe_face->reinit(elem, s); // Loop over the face quagrature points for integration. for (unsigned int qp=0; qp<qface->n_points(); qp++) { // The boundary value. Number value = exact_solution(qface_point[qp], es.parameters, "null", "void"); Gradient flux = exact_2D_derivative(qface_point[qp], es.parameters, "null", "void"); // Matrix contribution of the L2 projection. // Note that the basis function values are // integrated against test function values while // basis fluxes are integrated against test function // fluxes. for (unsigned int i=0; i<phi_face.size(); i++) for (unsigned int j=0; j<phi_face.size(); j++) Ke(i,j) += JxW_face[qp] * (penalty * phi_face[i][qp] * phi_face[j][qp] + penalty2 * (dphi_face[i][qp] * face_normals[qp]) * (dphi_face[j][qp] * face_normals[qp])); // Right-hand-side contribution of the L2 // projection. for (unsigned int i=0; i<phi_face.size(); i++) Fe(i) += JxW_face[qp] * (penalty * value * phi_face[i][qp] + penalty2 * (flux * face_normals[qp]) * (dphi_face[i][qp] * face_normals[qp])); } } // Stop logging the boundary condition computation perf_log.pop ("BCs"); } for (unsigned int qp=0; qp<qrule->n_points(); qp++) for (unsigned int i=0; i<phi.size(); i++) Fe(i) += JxW[qp]*phi[i][qp]*forcing_function(q_point[qp]); // The element matrix and right-hand-side are now built // for this element. Add them to the global matrix and // right-hand-side vector. The \p SparseMatrix::add_matrix() // and \p NumericVector::add_vector() members do this for us. // Start logging the insertion of the local (element) // matrix and vector into the global matrix and vector perf_log.push ("matrix insertion"); dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices); system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); // Stop logging the insertion of the local (element) // matrix and vector into the global matrix and vector perf_log.pop ("matrix insertion"); } // That's it. We don't need to do anything else to the // PerfLog. When it goes out of scope (at this function return) // it will print its log to the screen. Pretty easy, huh? #else #endif // #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES #endif // #ifdef LIBMESH_ENABLE_AMR }
// We now define the matrix assembly function for the // Poisson system. We need to first compute element // matrices and right-hand sides, and then take into // account the boundary conditions. void assemble_poisson(EquationSystems& es, const std::string& system_name) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Poisson"); // Declare a performance log. Give it a descriptive // string to identify what part of the code we are // logging, since there may be many PerfLogs in an // application. PerfLog perf_log ("Matrix Assembly"); // 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(); // Get a reference to the LinearImplicitSystem we are solving LinearImplicitSystem& system = es.get_system<LinearImplicitSystem>("Poisson"); // 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 UniquePtr<FEBase>. This can be thought // of as a pointer that will clean up after itself. UniquePtr<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. UniquePtr<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 physical XY locations of the quadrature points on the element. // These might be useful for evaluating spatially varying material // properties at the quadrature points. const std::vector<Point>& q_point = fe->get_xyz(); // 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 element matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke" and "Fe". More detail is in example 3. DenseMatrix<Number> Ke; DenseVector<Number> Fe; // 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 elements in the mesh. // We will compute the element matrix and right-hand-side // contribution. See example 3 for a discussion of the // element iterators. 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) { // Start logging the shape function initialization. // This is done through a simple function call with // the name of the event to log. perf_log.push("elem init"); // 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 matrix and right-hand side 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()); Fe.resize (dof_indices.size()); // Stop logging the shape function initialization. // If you forget to stop logging an event the PerfLog // object will probably catch the error and abort. perf_log.pop("elem init"); // Now we will build the element matrix. This involves // a double loop to integrate the test funcions (i) against // the trial functions (j). // // We have split the numeric integration into two loops // so that we can log the matrix and right-hand-side // computation seperately. // // Now start logging the element matrix computation perf_log.push ("Ke"); for (unsigned int qp=0; qp<qrule.n_points(); qp++) for (unsigned int i=0; i<phi.size(); i++) for (unsigned int j=0; j<phi.size(); j++) Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]); // Stop logging the matrix computation perf_log.pop ("Ke"); // Now we build the element right-hand-side contribution. // This involves a single loop in which we integrate the // "forcing function" in the PDE against the test functions. // // Start logging the right-hand-side computation perf_log.push ("Fe"); for (unsigned int qp=0; qp<qrule.n_points(); qp++) { // fxy is the forcing function for the Poisson equation. // In this case we set fxy to be a finite difference // Laplacian approximation to the (known) exact solution. // // We will use the second-order accurate FD Laplacian // approximation, which in 2D on a structured grid is // // u_xx + u_yy = (u(i-1,j) + u(i+1,j) + // u(i,j-1) + u(i,j+1) + // -4*u(i,j))/h^2 // // Since the value of the forcing function depends only // on the location of the quadrature point (q_point[qp]) // we will compute it here, outside of the i-loop const Real x = q_point[qp](0); #if LIBMESH_DIM > 1 const Real y = q_point[qp](1); #else const Real y = 0.; #endif #if LIBMESH_DIM > 2 const Real z = q_point[qp](2); #else const Real z = 0.; #endif const Real eps = 1.e-3; const Real uxx = (exact_solution(x-eps,y,z) + exact_solution(x+eps,y,z) + -2.*exact_solution(x,y,z))/eps/eps; const Real uyy = (exact_solution(x,y-eps,z) + exact_solution(x,y+eps,z) + -2.*exact_solution(x,y,z))/eps/eps; const Real uzz = (exact_solution(x,y,z-eps) + exact_solution(x,y,z+eps) + -2.*exact_solution(x,y,z))/eps/eps; Real fxy; if(dim==1) { // In 1D, compute the rhs by differentiating the // exact solution twice. const Real pi = libMesh::pi; fxy = (0.25*pi*pi)*sin(.5*pi*x); } else { fxy = - (uxx + uyy + ((dim==2) ? 0. : uzz)); } // Add the RHS contribution for (unsigned int i=0; i<phi.size(); i++) Fe(i) += JxW[qp]*fxy*phi[i][qp]; } // Stop logging the right-hand-side computation perf_log.pop ("Fe"); // If this assembly program were to be used on an adaptive mesh, // we would have to apply any hanging node constraint equations // Also, note that here we call heterogenously_constrain_element_matrix_and_vector // to impose a inhomogeneous Dirichlet boundary conditions. dof_map.heterogenously_constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // The element matrix and right-hand-side are now built // for this element. Add them to the global matrix and // right-hand-side vector. The \p SparseMatrix::add_matrix() // and \p NumericVector::add_vector() members do this for us. // Start logging the insertion of the local (element) // matrix and vector into the global matrix and vector perf_log.push ("matrix insertion"); system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); // Start logging the insertion of the local (element) // matrix and vector into the global matrix and vector perf_log.pop ("matrix insertion"); } // That's it. We don't need to do anything else to the // PerfLog. When it goes out of scope (at this function return) // it will print its log to the screen. Pretty easy, huh? }
void assemble_poisson(EquationSystems & es, const ElementIdMap & lower_to_upper) { const MeshBase & mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); Real R = es.parameters.get<Real>("R"); LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>("Poisson"); const DofMap & dof_map = system.get_dof_map(); FEType fe_type = dof_map.variable_type(0); UniquePtr<FEBase> fe (FEBase::build(dim, fe_type)); UniquePtr<FEBase> fe_elem_face (FEBase::build(dim, fe_type)); UniquePtr<FEBase> fe_neighbor_face (FEBase::build(dim, fe_type)); QGauss qrule (dim, fe_type.default_quadrature_order()); QGauss qface(dim-1, fe_type.default_quadrature_order()); fe->attach_quadrature_rule (&qrule); fe_elem_face->attach_quadrature_rule (&qface); fe_neighbor_face->attach_quadrature_rule (&qface); const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<std::vector<Real> > & phi = fe->get_phi(); const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi(); const std::vector<Real> & JxW_face = fe_elem_face->get_JxW(); const std::vector<Point> & qface_points = fe_elem_face->get_xyz(); const std::vector<std::vector<Real> > & phi_face = fe_elem_face->get_phi(); const std::vector<std::vector<Real> > & phi_neighbor_face = fe_neighbor_face->get_phi(); DenseMatrix<Number> Ke; DenseVector<Number> Fe; DenseMatrix<Number> Kne; DenseMatrix<Number> Ken; DenseMatrix<Number> Kee; DenseMatrix<Number> Knn; std::vector<dof_id_type> dof_indices; 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) { const Elem * elem = *el; dof_map.dof_indices (elem, dof_indices); const unsigned int n_dofs = dof_indices.size(); fe->reinit (elem); Ke.resize (n_dofs, n_dofs); Fe.resize (n_dofs); // Assemble element interior terms for the matrix for (unsigned int qp=0; qp<qrule.n_points(); qp++) for (unsigned int i=0; i<n_dofs; i++) for (unsigned int j=0; j<n_dofs; j++) Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]); // Boundary flux provides forcing in this example { for (unsigned int side=0; side<elem->n_sides(); side++) if (elem->neighbor(side) == NULL) { if (mesh.get_boundary_info().has_boundary_id (elem, side, MIN_Z_BOUNDARY)) { fe_elem_face->reinit(elem, side); for (unsigned int qp=0; qp<qface.n_points(); qp++) for (unsigned int i=0; i<phi.size(); i++) Fe(i) += JxW_face[qp] * phi_face[i][qp]; } } } // Add boundary terms on the crack { for (unsigned int side=0; side<elem->n_sides(); side++) if (elem->neighbor(side) == NULL) { // Found the lower side of the crack. Assemble terms due to lower and upper in here. if (mesh.get_boundary_info().has_boundary_id (elem, side, CRACK_BOUNDARY_LOWER)) { fe_elem_face->reinit(elem, side); ElementIdMap::const_iterator ltu_it = lower_to_upper.find(std::make_pair(elem->id(), side)); dof_id_type upper_elem_id = ltu_it->second; const Elem * neighbor = mesh.elem(upper_elem_id); std::vector<Point> qface_neighbor_points; FEInterface::inverse_map (elem->dim(), fe->get_fe_type(), neighbor, qface_points, qface_neighbor_points); fe_neighbor_face->reinit(neighbor, &qface_neighbor_points); std::vector<dof_id_type> neighbor_dof_indices; dof_map.dof_indices (neighbor, neighbor_dof_indices); const unsigned int n_neighbor_dofs = neighbor_dof_indices.size(); Kne.resize (n_neighbor_dofs, n_dofs); Ken.resize (n_dofs, n_neighbor_dofs); Kee.resize (n_dofs, n_dofs); Knn.resize (n_neighbor_dofs, n_neighbor_dofs); // Lower-to-lower coupling term for (unsigned int qp=0; qp<qface.n_points(); qp++) for (unsigned int i=0; i<n_dofs; i++) for (unsigned int j=0; j<n_dofs; j++) Kee(i,j) -= JxW_face[qp] * (1./R)*(phi_face[i][qp] * phi_face[j][qp]); // Lower-to-upper coupling term for (unsigned int qp=0; qp<qface.n_points(); qp++) for (unsigned int i=0; i<n_dofs; i++) for (unsigned int j=0; j<n_neighbor_dofs; j++) Ken(i,j) += JxW_face[qp] * (1./R)*(phi_face[i][qp] * phi_neighbor_face[j][qp]); // Upper-to-upper coupling term for (unsigned int qp=0; qp<qface.n_points(); qp++) for (unsigned int i=0; i<n_neighbor_dofs; i++) for (unsigned int j=0; j<n_neighbor_dofs; j++) Knn(i,j) -= JxW_face[qp] * (1./R)*(phi_neighbor_face[i][qp] * phi_neighbor_face[j][qp]); // Upper-to-lower coupling term for (unsigned int qp=0; qp<qface.n_points(); qp++) for (unsigned int i=0; i<n_neighbor_dofs; i++) for (unsigned int j=0; j<n_dofs; j++) Kne(i,j) += JxW_face[qp] * (1./R)*(phi_neighbor_face[i][qp] * phi_face[j][qp]); system.matrix->add_matrix(Kne, neighbor_dof_indices, dof_indices); system.matrix->add_matrix(Ken, dof_indices, neighbor_dof_indices); system.matrix->add_matrix(Kee, dof_indices); system.matrix->add_matrix(Knn, neighbor_dof_indices); } } } dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); } }
void MultiAppProjectionTransfer::assembleL2From(EquationSystems & es, const std::string & system_name) { unsigned int n_apps = _multi_app->numGlobalApps(); std::vector<NumericVector<Number> *> from_slns(n_apps, NULL); std::vector<MeshFunction *> from_fns(n_apps, NULL); std::vector<MeshTools::BoundingBox *> from_bbs(n_apps, NULL); // get bounding box, mesh function and solution for each subapp for (unsigned int i = 0; i < n_apps; i++) { if (!_multi_app->hasLocalApp(i)) continue; MPI_Comm swapped = Moose::swapLibMeshComm(_multi_app->comm()); FEProblem & from_problem = *_multi_app->appProblem(i); EquationSystems & from_es = from_problem.es(); MeshBase & from_mesh = from_es.get_mesh(); MeshTools::BoundingBox * app_box = new MeshTools::BoundingBox(MeshTools::processor_bounding_box(from_mesh, from_mesh.processor_id())); from_bbs[i] = app_box; MooseVariable & from_var = from_problem.getVariable(0, _from_var_name); System & from_sys = from_var.sys().system(); unsigned int from_var_num = from_sys.variable_number(from_var.name()); NumericVector<Number> * serialized_from_solution = NumericVector<Number>::build(from_sys.comm()).release(); serialized_from_solution->init(from_sys.n_dofs(), false, SERIAL); // Need to pull down a full copy of this vector on every processor so we can get values in parallel from_sys.solution->localize(*serialized_from_solution); from_slns[i] = serialized_from_solution; MeshFunction * from_func = new MeshFunction(from_es, *serialized_from_solution, from_sys.get_dof_map(), from_var_num); from_func->init(Trees::ELEMENTS); from_func->enable_out_of_mesh_mode(NOTFOUND); from_fns[i] = from_func; Moose::swapLibMeshComm(swapped); } const MeshBase& mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>(system_name); FEType fe_type = system.variable_type(0); AutoPtr<FEBase> fe(FEBase::build(dim, fe_type)); QGauss qrule(dim, fe_type.default_quadrature_order()); fe->attach_quadrature_rule(&qrule); const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<std::vector<Real> > & phi = fe->get_phi(); const std::vector<Point> & xyz = fe->get_xyz(); const DofMap& dof_map = system.get_dof_map(); DenseMatrix<Number> Ke; DenseVector<Number> Fe; std::vector<dof_id_type> dof_indices; 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) { const Elem* elem = *el; fe->reinit (elem); dof_map.dof_indices (elem, dof_indices); Ke.resize (dof_indices.size(), dof_indices.size()); Fe.resize (dof_indices.size()); for (unsigned int qp = 0; qp < qrule.n_points(); qp++) { Point qpt = xyz[qp]; Real f = 0.; for (unsigned int app = 0; app < n_apps; app++) { Point pt = qpt - _multi_app->position(app); if (from_bbs[app] != NULL && from_bbs[app]->contains_point(pt)) { MPI_Comm swapped = Moose::swapLibMeshComm(_multi_app->comm()); f = (*from_fns[app])(pt); Moose::swapLibMeshComm(swapped); break; } } // Now compute the element matrix and RHS contributions. for (unsigned int i=0; i<phi.size(); i++) { // RHS Fe(i) += JxW[qp] * (f * phi[i][qp]); if (_compute_matrix) for (unsigned int j = 0; j < phi.size(); j++) { // The matrix contribution Ke(i,j) += JxW[qp] * (phi[i][qp] * phi[j][qp]); } } dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices); if (_compute_matrix) system.matrix->add_matrix(Ke, dof_indices); system.rhs->add_vector(Fe, dof_indices); } } for (unsigned int i = 0; i < n_apps; i++) { delete from_fns[i]; delete from_bbs[i]; delete from_slns[i]; } }
// This function assembles the system matrix and right-hand-side // for the discrete form of our wave equation. void assemble_wave(EquationSystems & es, const std::string & system_name) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Wave"); #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS // Get a constant reference to the mesh object. const MeshBase & mesh = es.get_mesh(); // Get a reference to the system we are solving. LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>("Wave"); // 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. const DofMap & dof_map = system.get_dof_map(); // The dimension that we are running. const unsigned int dim = mesh.mesh_dimension(); // Copy the speed of sound to a local variable. const Real speed = es.parameters.get<Real>("speed"); // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. const 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 UniquePtr<FEBase>. Check ex5 for details. UniquePtr<FEBase> fe (FEBase::build(dim, fe_type)); // Do the same for an infinite element. UniquePtr<FEBase> inf_fe (FEBase::build_InfFE(dim, fe_type)); // A 2nd order Gauss quadrature rule for numerical integration. QGauss qrule (dim, SECOND); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (&qrule); // Due to its internal structure, the infinite element handles // quadrature rules differently. It takes the quadrature // rule which has been initialized for the FE object, but // creates suitable quadrature rules by @e itself. The user // need not worry about this. inf_fe->attach_quadrature_rule (&qrule); // Define data structures to contain the element matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke", "Ce", "Me", and "Fe" for the stiffness, damping // and mass matrices, and the load vector. Note that in // Acoustics, these descriptors though do @e not match the // true physical meaning of the projectors. The final // overall system, however, resembles the conventional // notation again. DenseMatrix<Number> Ke; DenseMatrix<Number> Ce; DenseMatrix<Number> Me; DenseVector<Number> Fe; // 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 elements in the mesh. // We will compute the element matrix and right-hand-side // 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); // The mesh contains both finite and infinite elements. These // elements are handled through different classes, namely // \p FE and \p InfFE, respectively. However, since both // are derived from \p FEBase, they share the same interface, // and overall burden of coding is @e greatly reduced through // using a pointer, which is adjusted appropriately to the // current element type. FEBase * cfe = libmesh_nullptr; // This here is almost the only place where we need to // distinguish between finite and infinite elements. // For faster computation, however, different approaches // may be feasible. // // Up to now, we do not know what kind of element we // have. Aske the element of what type it is: if (elem->infinite()) { // We have an infinite element. Let \p cfe point // to our \p InfFE object. This is handled through // an UniquePtr. Through the \p UniquePtr::get() we "borrow" // the pointer, while the \p UniquePtr \p inf_fe is // still in charge of memory management. cfe = inf_fe.get(); } else { // This is a conventional finite element. Let \p fe handle it. cfe = fe.get(); // Boundary conditions. // Here we just zero the rhs-vector. For natural boundary // conditions check e.g. previous examples. { // Zero the RHS for this element. Fe.resize (dof_indices.size()); system.rhs->add_vector (Fe, dof_indices); } // end boundary condition section } // else if (elem->infinite()) // This is slightly different from the Poisson solver: // Since the finite element object may change, we have to // initialize the constant references to the data fields // each time again, when a new element is processed. // // The element Jacobian * quadrature weight at each integration point. const std::vector<Real> & JxW = cfe->get_JxW(); // The element shape functions evaluated at the quadrature points. const std::vector<std::vector<Real> > & phi = cfe->get_phi(); // The element shape function gradients evaluated at the quadrature // points. const std::vector<std::vector<RealGradient> > & dphi = cfe->get_dphi(); // The infinite elements need more data fields than conventional FE. // These are the gradients of the phase term \p dphase, an additional // radial weight for the test functions \p Sobolev_weight, and its // gradient. // // Note that these data fields are also initialized appropriately by // the \p FE method, so that the weak form (below) is valid for @e both // finite and infinite elements. const std::vector<RealGradient> & dphase = cfe->get_dphase(); const std::vector<Real> & weight = cfe->get_Sobolev_weight(); const std::vector<RealGradient> & dweight = cfe->get_Sobolev_dweight(); // Now this is all independent of whether we use an \p FE // or an \p InfFE. Nice, hm? ;-) // // Compute the element-specific data, as described // in previous examples. cfe->reinit (elem); // Zero the element matrices. Boundary conditions were already // processed in the \p FE-only section, see above. Ke.resize (dof_indices.size(), dof_indices.size()); Ce.resize (dof_indices.size(), dof_indices.size()); Me.resize (dof_indices.size(), dof_indices.size()); // The total number of quadrature points for infinite elements // @e has to be determined in a different way, compared to // conventional finite elements. This type of access is also // valid for finite elements, so this can safely be used // anytime, instead of asking the quadrature rule, as // seen in previous examples. unsigned int max_qp = cfe->n_quadrature_points(); // Loop over the quadrature points. for (unsigned int qp=0; qp<max_qp; qp++) { // Similar to the modified access to the number of quadrature // points, the number of shape functions may also be obtained // in a different manner. This offers the great advantage // of being valid for both finite and infinite elements. const unsigned int n_sf = cfe->n_shape_functions(); // Now we will build the element matrices. Since the infinite // elements are based on a Petrov-Galerkin scheme, the // resulting system matrices are non-symmetric. The additional // weight, described before, is part of the trial space. // // For the finite elements, though, these matrices are symmetric // just as we know them, since the additional fields \p dphase, // \p weight, and \p dweight are initialized appropriately. // // test functions: weight[qp]*phi[i][qp] // trial functions: phi[j][qp] // phase term: phase[qp] // // derivatives are similar, but note that these are of type // Point, not of type Real. for (unsigned int i=0; i<n_sf; i++) for (unsigned int j=0; j<n_sf; j++) { // (ndt*Ht + nHt*d) * nH Ke(i,j) += ( (dweight[qp] * phi[i][qp] // Point * Real = Point + // + dphi[i][qp] * weight[qp] // Point * Real = Point ) * dphi[j][qp] ) * JxW[qp]; // (d*Ht*nmut*nH - ndt*nmu*Ht*H - d*nHt*nmu*H) Ce(i,j) += ( (dphase[qp] * dphi[j][qp]) // (Point * Point) = Real * weight[qp] * phi[i][qp] // * Real * Real = Real - // - (dweight[qp] * dphase[qp]) // (Point * Point) = Real * phi[i][qp] * phi[j][qp] // * Real * Real = Real - // - (dphi[i][qp] * dphase[qp]) // (Point * Point) = Real * weight[qp] * phi[j][qp] // * Real * Real = Real ) * JxW[qp]; // (d*Ht*H * (1 - nmut*nmu)) Me(i,j) += ( (1. - (dphase[qp] * dphase[qp])) // (Real - (Point * Point)) = Real * phi[i][qp] * phi[j][qp] * weight[qp] // * Real * Real * Real = Real ) * JxW[qp]; } // end of the matrix summation loop } // end of quadrature point loop // The element matrices are now built for this element. // Collect them in Ke, and then add them to the global matrix. // The \p SparseMatrix::add_matrix() member does this for us. Ke.add(1./speed , Ce); Ke.add(1./(speed*speed), Me); // If this assembly program were to be used on an adaptive mesh, // we would have to apply any hanging node constraint equations dof_map.constrain_element_matrix(Ke, dof_indices); system.matrix->add_matrix (Ke, dof_indices); } // end of element loop // Note that we have not applied any boundary conditions so far. // Here we apply a unit load at the node located at (0,0,0). { // Iterate over local nodes MeshBase::const_node_iterator nd = mesh.local_nodes_begin(); const MeshBase::const_node_iterator nd_end = mesh.local_nodes_end(); for (; nd != nd_end; ++nd) { // Get a reference to the current node. const Node & node = **nd; // Check the location of the current node. if (std::abs(node(0)) < TOLERANCE && std::abs(node(1)) < TOLERANCE && std::abs(node(2)) < TOLERANCE) { // The global number of the respective degree of freedom. unsigned int dn = node.dof_number(0,0,0); system.rhs->add (dn, 1.); } } } #else // dummy assert libmesh_assert_not_equal_to (es.get_mesh().mesh_dimension(), 1); #endif //ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS }
void assemble_elasticity(EquationSystems & es, const std::string & libmesh_dbg_var(system_name)) { libmesh_assert_equal_to (system_name, "Elasticity"); const MeshBase & mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>("Elasticity"); const unsigned int u_var = system.variable_number ("u"); const unsigned int v_var = system.variable_number ("v"); const DofMap & dof_map = system.get_dof_map(); FEType fe_type = dof_map.variable_type(0); UniquePtr<FEBase> fe (FEBase::build(dim, fe_type)); QGauss qrule (dim, fe_type.default_quadrature_order()); fe->attach_quadrature_rule (&qrule); UniquePtr<FEBase> fe_face (FEBase::build(dim, fe_type)); QGauss qface(dim-1, fe_type.default_quadrature_order()); fe_face->attach_quadrature_rule (&qface); const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi(); DenseMatrix<Number> Ke; DenseVector<Number> Fe; DenseSubMatrix<Number> Kuu(Ke), Kuv(Ke), Kvu(Ke), Kvv(Ke); DenseSubVector<Number> Fu(Fe), Fv(Fe); std::vector<dof_id_type> dof_indices; std::vector<dof_id_type> dof_indices_u; std::vector<dof_id_type> dof_indices_v; 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) { const Elem * elem = *el; dof_map.dof_indices (elem, dof_indices); dof_map.dof_indices (elem, dof_indices_u, u_var); dof_map.dof_indices (elem, dof_indices_v, v_var); const unsigned int n_dofs = dof_indices.size(); const unsigned int n_u_dofs = dof_indices_u.size(); const unsigned int n_v_dofs = dof_indices_v.size(); fe->reinit (elem); Ke.resize (n_dofs, n_dofs); Fe.resize (n_dofs); Kuu.reposition (u_var*n_u_dofs, u_var*n_u_dofs, n_u_dofs, n_u_dofs); Kuv.reposition (u_var*n_u_dofs, v_var*n_u_dofs, n_u_dofs, n_v_dofs); Kvu.reposition (v_var*n_v_dofs, u_var*n_v_dofs, n_v_dofs, n_u_dofs); Kvv.reposition (v_var*n_v_dofs, v_var*n_v_dofs, n_v_dofs, n_v_dofs); Fu.reposition (u_var*n_u_dofs, n_u_dofs); Fv.reposition (v_var*n_u_dofs, n_v_dofs); for (unsigned int qp=0; qp<qrule.n_points(); qp++) { for (unsigned int i=0; i<n_u_dofs; i++) for (unsigned int j=0; j<n_u_dofs; j++) { // Tensor indices unsigned int C_i, C_j, C_k, C_l; C_i=0, C_k=0; C_j=0, C_l=0; Kuu(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=1, C_l=0; Kuu(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=0, C_l=1; Kuu(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=1, C_l=1; Kuu(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); } for (unsigned int i=0; i<n_u_dofs; i++) for (unsigned int j=0; j<n_v_dofs; j++) { // Tensor indices unsigned int C_i, C_j, C_k, C_l; C_i=0, C_k=1; C_j=0, C_l=0; Kuv(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=1, C_l=0; Kuv(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=0, C_l=1; Kuv(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=1, C_l=1; Kuv(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); } for (unsigned int i=0; i<n_v_dofs; i++) for (unsigned int j=0; j<n_u_dofs; j++) { // Tensor indices unsigned int C_i, C_j, C_k, C_l; C_i=1, C_k=0; C_j=0, C_l=0; Kvu(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=1, C_l=0; Kvu(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=0, C_l=1; Kvu(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=1, C_l=1; Kvu(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); } for (unsigned int i=0; i<n_v_dofs; i++) for (unsigned int j=0; j<n_v_dofs; j++) { // Tensor indices unsigned int C_i, C_j, C_k, C_l; C_i=1, C_k=1; C_j=0, C_l=0; Kvv(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=1, C_l=0; Kvv(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=0, C_l=1; Kvv(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); C_j=1, C_l=1; Kvv(i,j) += JxW[qp]*(eval_elasticity_tensor(C_i, C_j, C_k, C_l) * dphi[i][qp](C_j)*dphi[j][qp](C_l)); } } { for (unsigned int side=0; side<elem->n_sides(); side++) if (elem->neighbor_ptr(side) == libmesh_nullptr) { const std::vector<std::vector<Real> > & phi_face = fe_face->get_phi(); const std::vector<Real> & JxW_face = fe_face->get_JxW(); fe_face->reinit(elem, side); if (mesh.get_boundary_info().has_boundary_id (elem, side, 1)) // Apply a traction on the right side { for (unsigned int qp=0; qp<qface.n_points(); qp++) for (unsigned int i=0; i<n_v_dofs; i++) Fv(i) += JxW_face[qp] * (-1.) * phi_face[i][qp]; } } } dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); } }
void assemble(EquationSystems& es, const std::string& libmesh_dbg_var(system_name)) { libmesh_assert_equal_to (system_name, "primary"); const MeshBase& mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); // Also use a 3x3x3 quadrature rule (3D). Then tell the FE // about the geometry of the problem and the quadrature rule FEType fe_type (FIRST); UniquePtr<FEBase> fe(FEBase::build(dim, fe_type)); QGauss qrule(dim, FIFTH); fe->attach_quadrature_rule (&qrule); UniquePtr<FEBase> fe_face(FEBase::build(dim, fe_type)); QGauss qface(dim-1, FIFTH); fe_face->attach_quadrature_rule(&qface); LinearImplicitSystem& system = es.get_system<LinearImplicitSystem>("primary"); // These are references to cell-specific data const std::vector<Real>& JxW_face = fe_face->get_JxW(); const std::vector<Real>& JxW = fe->get_JxW(); const std::vector<Point>& q_point = fe->get_xyz(); const std::vector<std::vector<Real> >& phi = fe->get_phi(); const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi(); std::vector<unsigned int> dof_indices_U; std::vector<unsigned int> dof_indices_V; const DofMap& dof_map = system.get_dof_map(); DenseMatrix<Number> Kuu; DenseMatrix<Number> Kvv; DenseVector<Number> Fu; DenseVector<Number> Fv; Real vol=0., area=0.; 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) { const Elem* elem = *el; // recompute the element-specific data for the current element fe->reinit (elem); //fe->print_info(); dof_map.dof_indices(elem, dof_indices_U, 0); dof_map.dof_indices(elem, dof_indices_V, 1); // zero the element matrix and vector Kuu.resize (phi.size(), phi.size()); Kvv.resize (phi.size(), phi.size()); Fu.resize (phi.size()); Fv.resize (phi.size()); // standard stuff... like in code 1. for (unsigned int gp=0; gp<qrule.n_points(); gp++) { for (unsigned int i=0; i<phi.size(); ++i) { // this is tricky. ig is the _global_ dof index corresponding // to the _global_ vertex number elem->node(i). Note that // in general these numbers will not be the same (except for // the case of one unknown per node on one subdomain) so // we need to go through the dof_map const Real f = q_point[gp]*q_point[gp]; // const Real f = (q_point[gp](0) + // q_point[gp](1) + // q_point[gp](2)); // add jac*weight*f*phi to the RHS in position ig Fu(i) += JxW[gp]*f*phi[i][gp]; Fv(i) += JxW[gp]*f*phi[i][gp]; for (unsigned int j=0; j<phi.size(); ++j) { Kuu(i,j) += JxW[gp]*((phi[i][gp])*(phi[j][gp])); Kvv(i,j) += JxW[gp]*((phi[i][gp])*(phi[j][gp]) + 1.*((dphi[i][gp])*(dphi[j][gp]))); }; }; vol += JxW[gp]; }; // You can't compute "area" (perimeter) if you are in 2D if (dim == 3) { for (unsigned int side=0; side<elem->n_sides(); side++) if (elem->neighbor(side) == NULL) { fe_face->reinit (elem, side); //fe_face->print_info(); for (unsigned int gp=0; gp<JxW_face.size(); gp++) area += JxW_face[gp]; } } // Constrain the DOF indices. dof_map.constrain_element_matrix_and_vector(Kuu, Fu, dof_indices_U); dof_map.constrain_element_matrix_and_vector(Kvv, Fv, dof_indices_V); system.rhs->add_vector(Fu, dof_indices_U); system.rhs->add_vector(Fv, dof_indices_V); system.matrix->add_matrix(Kuu, dof_indices_U); system.matrix->add_matrix(Kvv, dof_indices_V); } libMesh::out << "Vol=" << vol << std::endl; if (dim == 3) libMesh::out << "Area=" << area << std::endl; }
// Define the matrix assembly function for the 1D PDE we are solving void assemble_1D(EquationSystems& es, const std::string& system_name) { #ifdef LIBMESH_ENABLE_AMR // It is a good idea to check we are solving the correct system libmesh_assert_equal_to (system_name, "1D"); // Get a reference to the mesh object const MeshBase& mesh = es.get_mesh(); // The dimension we are using, i.e. dim==1 const unsigned int dim = mesh.mesh_dimension(); // Get a reference to the system we are solving LinearImplicitSystem& system = es.get_system<LinearImplicitSystem>("1D"); // Get a reference to the DofMap object for this system. The DofMap object // handles the index translation from node and element numbers to degree of // freedom numbers. DofMap's are discussed in more detail 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. The build // function dynamically allocates memory so we use an UniquePtr in this case. // An UniquePtr is a pointer that cleans up after itself. See examples 3 and 4 // for more details on UniquePtr. UniquePtr<FEBase> fe(FEBase::build(dim, fe_type)); // Tell the finite element object to use fifth order Gaussian quadrature QGauss qrule(dim,FIFTH); fe->attach_quadrature_rule(&qrule); // Here we define some references to cell-specific data that will be used to // assemble the linear system. // 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(); // Declare a dense matrix and dense vector to hold the element matrix // and right-hand-side contribution DenseMatrix<Number> Ke; DenseVector<Number> Fe; // 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; // We now loop over all the active elements in the mesh in order to calculate // the matrix and right-hand-side contribution from each element. Use a // const_element_iterator to loop over the elements. We make // el_end const as it is used only for the stopping condition of the loop. MeshBase::const_element_iterator el = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator el_end = mesh.active_local_elements_end(); // Note that ++el is preferred to el++ when using loops with iterators for( ; el != el_end; ++el) { // It is convenient to store a pointer to the current element 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); // Store the number of local degrees of freedom contained in this element const int n_dofs = dof_indices.size(); // We resize and zero out Ke and Fe (resize() also clears the matrix and // vector). In this example, all elements in the mesh are EDGE3's, so // Ke will always be 3x3, and Fe will always be 3x1. If the mesh contained // different element types, then the size of Ke and Fe would change. Ke.resize(n_dofs, n_dofs); Fe.resize(n_dofs); // Now loop over quadrature points to handle numerical integration for(unsigned int qp=0; qp<qrule.n_points(); qp++) { // Now build the element matrix and right-hand-side using loops to // integrate the test functions (i) against the trial functions (j). for(unsigned int i=0; i<phi.size(); i++) { Fe(i) += JxW[qp]*phi[i][qp]; for(unsigned int j=0; j<phi.size(); j++) { Ke(i,j) += JxW[qp]*(1.e-3*dphi[i][qp]*dphi[j][qp] + phi[i][qp]*phi[j][qp]); } } } // At this point we have completed the matrix and RHS summation. The // final step is to apply boundary conditions, which in this case are // simple Dirichlet conditions with u(0) = u(1) = 0. // Define the penalty parameter used to enforce the BC's double penalty = 1.e10; // Loop over the sides of this element. For a 1D element, the "sides" // are defined as the nodes on each edge of the element, i.e. 1D elements // have 2 sides. for(unsigned int s=0; s<elem->n_sides(); s++) { // If this element has a NULL neighbor, then it is on the edge of the // mesh and we need to enforce a boundary condition using the penalty // method. if(elem->neighbor(s) == NULL) { Ke(s,s) += penalty; Fe(s) += 0*penalty; } } // This is a function call that is necessary when using adaptive // mesh refinement. See Adaptivity Example 2 for more details. dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // Add Ke and Fe to the global matrix and right-hand-side. system.matrix->add_matrix(Ke, dof_indices); system.rhs->add_vector(Fe, dof_indices); } #endif // #ifdef LIBMESH_ENABLE_AMR }
// We now define the matrix and rhs vector assembly function // for the shell system. This function implements the // linear Kirchhoff-Love theory for thin shells. At the // end we also take into account the boundary conditions // here, using the penalty method. void assemble_shell (EquationSystems & es, const std::string & libmesh_dbg_var(system_name)) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Shell"); // Get a constant reference to the mesh object. const MeshBase & mesh = es.get_mesh(); // Get a reference to the shell system object. LinearImplicitSystem & system = es.get_system<LinearImplicitSystem> ("Shell"); // Get the shell parameters that we need during assembly. const Real h = es.parameters.get<Real> ("thickness"); const Real E = es.parameters.get<Real> ("young's modulus"); const Real nu = es.parameters.get<Real> ("poisson ratio"); const Real q = es.parameters.get<Real> ("uniform load"); // Compute the membrane stiffness K and the bending // rigidity D from these parameters. const Real K = E * h / (1-nu*nu); const Real D = E * h*h*h / (12*(1-nu*nu)); // Numeric ids corresponding to each variable in the system. const unsigned int u_var = system.variable_number ("u"); const unsigned int v_var = system.variable_number ("v"); const unsigned int w_var = system.variable_number ("w"); // Get the Finite Element type for "u". Note this will be // the same as the type for "v" and "w". FEType fe_type = system.variable_type (u_var); // Build a Finite Element object of the specified type. std::unique_ptr<FEBase> fe (FEBase::build(2, fe_type)); // A Gauss quadrature rule for numerical integration. // For subdivision shell elements, a single Gauss point per // element is sufficient, hence we use extraorder = 0. const int extraorder = 0; std::unique_ptr<QBase> qrule (fe_type.default_quadrature_rule (2, extraorder)); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (qrule.get()); // The element Jacobian * quadrature weight at each integration point. const std::vector<Real> & JxW = fe->get_JxW(); // The surface tangents in both directions at the quadrature points. const std::vector<RealGradient> & dxyzdxi = fe->get_dxyzdxi(); const std::vector<RealGradient> & dxyzdeta = fe->get_dxyzdeta(); // The second partial derivatives at the quadrature points. const std::vector<RealGradient> & d2xyzdxi2 = fe->get_d2xyzdxi2(); const std::vector<RealGradient> & d2xyzdeta2 = fe->get_d2xyzdeta2(); const std::vector<RealGradient> & d2xyzdxideta = fe->get_d2xyzdxideta(); // The element shape function and its derivatives evaluated at the // quadrature points. const std::vector<std::vector<Real>> & phi = fe->get_phi(); const std::vector<std::vector<RealGradient>> & dphi = fe->get_dphi(); const std::vector<std::vector<RealTensor>> & d2phi = fe->get_d2phi(); // A reference to the DofMap object for this system. The DofMap // object handles the index translation from node and element numbers // to degree of freedom numbers. const DofMap & dof_map = system.get_dof_map(); // Define data structures to contain the element stiffness matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke" and "Fe". DenseMatrix<Number> Ke; DenseVector<Number> Fe; DenseSubMatrix<Number> Kuu(Ke), Kuv(Ke), Kuw(Ke), Kvu(Ke), Kvv(Ke), Kvw(Ke), Kwu(Ke), Kwv(Ke), Kww(Ke); DenseSubVector<Number> Fu(Fe), Fv(Fe), Fw(Fe); // 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; std::vector<dof_id_type> dof_indices_u; std::vector<dof_id_type> dof_indices_v; std::vector<dof_id_type> dof_indices_w; // Now we will loop over all the elements in the mesh. We will // compute the element matrix and right-hand-side contribution. for (const auto & elem : mesh.active_local_element_ptr_range()) { // The ghost elements at the boundaries need to be excluded // here, as they don't belong to the physical shell, // but serve for a proper boundary treatment only. libmesh_assert_equal_to (elem->type(), TRI3SUBDIVISION); const Tri3Subdivision * sd_elem = static_cast<const Tri3Subdivision *> (elem); if (sd_elem->is_ghost()) continue; // 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); dof_map.dof_indices (elem, dof_indices_u, u_var); dof_map.dof_indices (elem, dof_indices_v, v_var); dof_map.dof_indices (elem, dof_indices_w, w_var); const std::size_t n_dofs = dof_indices.size(); const std::size_t n_u_dofs = dof_indices_u.size(); const std::size_t n_v_dofs = dof_indices_v.size(); const std::size_t n_w_dofs = dof_indices_w.size(); // Compute the element-specific data for the current // element. This involves computing the location of the // quadrature points and the shape functions // (phi, dphi, d2phi) for the current element. fe->reinit (elem); // Zero the element matrix and right-hand side before // summing them. We use the resize member here because // the number of degrees of freedom might have changed from // the last element. Ke.resize (n_dofs, n_dofs); Fe.resize (n_dofs); // Reposition the submatrices... The idea is this: // // - - - - // | Kuu Kuv Kuw | | Fu | // Ke = | Kvu Kvv Kvw |; Fe = | Fv | // | Kwu Kwv Kww | | Fw | // - - - - // // The DenseSubMatrix.reposition () member takes the // (row_offset, column_offset, row_size, column_size). // // Similarly, the DenseSubVector.reposition () member // takes the (row_offset, row_size) Kuu.reposition (u_var*n_u_dofs, u_var*n_u_dofs, n_u_dofs, n_u_dofs); Kuv.reposition (u_var*n_u_dofs, v_var*n_u_dofs, n_u_dofs, n_v_dofs); Kuw.reposition (u_var*n_u_dofs, w_var*n_u_dofs, n_u_dofs, n_w_dofs); Kvu.reposition (v_var*n_v_dofs, u_var*n_v_dofs, n_v_dofs, n_u_dofs); Kvv.reposition (v_var*n_v_dofs, v_var*n_v_dofs, n_v_dofs, n_v_dofs); Kvw.reposition (v_var*n_v_dofs, w_var*n_v_dofs, n_v_dofs, n_w_dofs); Kwu.reposition (w_var*n_w_dofs, u_var*n_w_dofs, n_w_dofs, n_u_dofs); Kwv.reposition (w_var*n_w_dofs, v_var*n_w_dofs, n_w_dofs, n_v_dofs); Kww.reposition (w_var*n_w_dofs, w_var*n_w_dofs, n_w_dofs, n_w_dofs); Fu.reposition (u_var*n_u_dofs, n_u_dofs); Fv.reposition (v_var*n_u_dofs, n_v_dofs); Fw.reposition (w_var*n_u_dofs, n_w_dofs); // Now we will build the element matrix and right-hand-side. for (unsigned int qp=0; qp<qrule->n_points(); ++qp) { // First, we compute the external force resulting // from a load q distributed uniformly across the plate. // Since the load is supposed to be transverse to the plate, // it affects the z-direction, i.e. the "w" variable. for (unsigned int i=0; i<n_u_dofs; ++i) Fw(i) += JxW[qp] * phi[i][qp] * q; // Next, we assemble the stiffness matrix. This is only valid // for the linear theory, i.e., for small deformations, where // reference and deformed surface metrics are indistinguishable. // Get the three surface basis vectors. const RealVectorValue & a1 = dxyzdxi[qp]; const RealVectorValue & a2 = dxyzdeta[qp]; RealVectorValue a3 = a1.cross(a2); const Real jac = a3.norm(); // the surface Jacobian libmesh_assert_greater (jac, 0); a3 /= jac; // the shell director a3 is normalized to unit length // Get the derivatives of the surface tangents. const RealVectorValue & a11 = d2xyzdxi2[qp]; const RealVectorValue & a22 = d2xyzdeta2[qp]; const RealVectorValue & a12 = d2xyzdxideta[qp]; // Compute the three covariant components of the first // fundamental form of the surface. const RealVectorValue a(a1*a1, a2*a2, a1*a2); // The elastic H matrix in Voigt's notation, computed from the // covariant components of the first fundamental form rather // than the contravariant components, exploiting that the // contravariant first fundamental form is the inverse of the // covariant first fundamental form (hence the determinant etc.). RealTensorValue H; H(0,0) = a(1) * a(1); H(0,1) = H(1,0) = nu * a(1) * a(0) + (1-nu) * a(2) * a(2); H(0,2) = H(2,0) = -a(1) * a(2); H(1,1) = a(0) * a(0); H(1,2) = H(2,1) = -a(0) * a(2); H(2,2) = 0.5 * ((1-nu) * a(1) * a(0) + (1+nu) * a(2) * a(2)); const Real det = a(0) * a(1) - a(2) * a(2); libmesh_assert_not_equal_to (det * det, 0); H /= det * det; // Precompute come cross products for the bending part below. const RealVectorValue a11xa2 = a11.cross(a2); const RealVectorValue a22xa2 = a22.cross(a2); const RealVectorValue a12xa2 = a12.cross(a2); const RealVectorValue a1xa11 = a1.cross(a11); const RealVectorValue a1xa22 = a1.cross(a22); const RealVectorValue a1xa12 = a1.cross(a12); const RealVectorValue a2xa3 = a2.cross(a3); const RealVectorValue a3xa1 = a3.cross(a1); // Loop over all pairs of nodes I,J. for (unsigned int i=0; i<n_u_dofs; ++i) { for (unsigned int j=0; j<n_u_dofs; ++j) { // The membrane strain matrices in Voigt's notation. RealTensorValue MI, MJ; for (unsigned int k=0; k<3; ++k) { MI(0,k) = dphi[i][qp](0) * a1(k); MI(1,k) = dphi[i][qp](1) * a2(k); MI(2,k) = dphi[i][qp](1) * a1(k) + dphi[i][qp](0) * a2(k); MJ(0,k) = dphi[j][qp](0) * a1(k); MJ(1,k) = dphi[j][qp](1) * a2(k); MJ(2,k) = dphi[j][qp](1) * a1(k) + dphi[j][qp](0) * a2(k); } // The bending strain matrices in Voigt's notation. RealTensorValue BI, BJ; for (unsigned int k=0; k<3; ++k) { const Real term_ik = dphi[i][qp](0) * a2xa3(k) + dphi[i][qp](1) * a3xa1(k); BI(0,k) = -d2phi[i][qp](0,0) * a3(k) +(dphi[i][qp](0) * a11xa2(k) + dphi[i][qp](1) * a1xa11(k) + (a3*a11) * term_ik) / jac; BI(1,k) = -d2phi[i][qp](1,1) * a3(k) +(dphi[i][qp](0) * a22xa2(k) + dphi[i][qp](1) * a1xa22(k) + (a3*a22) * term_ik) / jac; BI(2,k) = 2 * (-d2phi[i][qp](0,1) * a3(k) +(dphi[i][qp](0) * a12xa2(k) + dphi[i][qp](1) * a1xa12(k) + (a3*a12) * term_ik) / jac); const Real term_jk = dphi[j][qp](0) * a2xa3(k) + dphi[j][qp](1) * a3xa1(k); BJ(0,k) = -d2phi[j][qp](0,0) * a3(k) +(dphi[j][qp](0) * a11xa2(k) + dphi[j][qp](1) * a1xa11(k) + (a3*a11) * term_jk) / jac; BJ(1,k) = -d2phi[j][qp](1,1) * a3(k) +(dphi[j][qp](0) * a22xa2(k) + dphi[j][qp](1) * a1xa22(k) + (a3*a22) * term_jk) / jac; BJ(2,k) = 2 * (-d2phi[j][qp](0,1) * a3(k) +(dphi[j][qp](0) * a12xa2(k) + dphi[j][qp](1) * a1xa12(k) + (a3*a12) * term_jk) / jac); } // The total stiffness matrix coupling the nodes // I and J is a sum of membrane and bending // contributions according to the following formula. const RealTensorValue KIJ = JxW[qp] * K * MI.transpose() * H * MJ + JxW[qp] * D * BI.transpose() * H * BJ; // Insert the components of the coupling stiffness // matrix KIJ into the corresponding directional // submatrices. Kuu(i,j) += KIJ(0,0); Kuv(i,j) += KIJ(0,1); Kuw(i,j) += KIJ(0,2); Kvu(i,j) += KIJ(1,0); Kvv(i,j) += KIJ(1,1); Kvw(i,j) += KIJ(1,2); Kwu(i,j) += KIJ(2,0); Kwv(i,j) += KIJ(2,1); Kww(i,j) += KIJ(2,2); } } } // end of the quadrature point qp-loop // The element matrix and right-hand-side are now built // for this element. Add them to the global matrix and // right-hand-side vector. The NumericMatrix::add_matrix() // and NumericVector::add_vector() members do this for us. system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); } // end of non-ghost element loop // Next, we apply the boundary conditions. In this case, // all boundaries are clamped by the penalty method, using // the special "ghost" nodes along the boundaries. Note // that there are better ways to implement boundary conditions // for subdivision shells. We use the simplest way here, // which is known to be overly restrictive and will lead to // a slightly too small deformation of the plate. for (const auto & elem : mesh.active_local_element_ptr_range()) { // For the boundary conditions, we only need to loop over // the ghost elements. libmesh_assert_equal_to (elem->type(), TRI3SUBDIVISION); const Tri3Subdivision * gh_elem = static_cast<const Tri3Subdivision *> (elem); if (!gh_elem->is_ghost()) continue; // Find the side which is part of the physical plate boundary, // that is, the boundary of the original mesh without ghosts. for (auto s : elem->side_index_range()) { const Tri3Subdivision * nb_elem = static_cast<const Tri3Subdivision *> (elem->neighbor_ptr(s)); if (nb_elem == libmesh_nullptr || nb_elem->is_ghost()) continue; /* * Determine the four nodes involved in the boundary * condition treatment of this side. The MeshTools::Subdiv * namespace provides lookup tables next and prev * for an efficient determination of the next and previous * nodes of an element, respectively. * * n4 * / \ * / gh \ * n2 ---- n3 * \ nb / * \ / * n1 */ const Node * nodes [4]; // n1, n2, n3, n4 nodes[1] = gh_elem->node_ptr(s); // n2 nodes[2] = gh_elem->node_ptr(MeshTools::Subdivision::next[s]); // n3 nodes[3] = gh_elem->node_ptr(MeshTools::Subdivision::prev[s]); // n4 // The node in the interior of the domain, n1, is the // hardest to find. Walk along the edges of element nb until // we have identified it. unsigned int n_int = 0; nodes[0] = nb_elem->node_ptr(0); while (nodes[0]->id() == nodes[1]->id() || nodes[0]->id() == nodes[2]->id()) nodes[0] = nb_elem->node_ptr(++n_int); // The penalty value. \f$ \frac{1}{\epsilon} \f$ const Real penalty = 1.e10; // With this simple method, clamped boundary conditions are // obtained by penalizing the displacements of all four nodes. // This ensures that the displacement field vanishes on the // boundary side s. for (unsigned int n=0; n<4; ++n) { const dof_id_type u_dof = nodes[n]->dof_number (system.number(), u_var, 0); const dof_id_type v_dof = nodes[n]->dof_number (system.number(), v_var, 0); const dof_id_type w_dof = nodes[n]->dof_number (system.number(), w_var, 0); system.matrix->add (u_dof, u_dof, penalty); system.matrix->add (v_dof, v_dof, penalty); system.matrix->add (w_dof, w_dof, penalty); } } } // end of ghost element loop }
// The matrix assembly function to be called at each time step to // prepare for the linear solve. void assemble_stokes (EquationSystems & es, const std::string & system_name) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Navier-Stokes"); // 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(); // Get a reference to the Stokes system object. TransientLinearImplicitSystem & navier_stokes_system = es.get_system<TransientLinearImplicitSystem> ("Navier-Stokes"); // Numeric ids corresponding to each variable in the system const unsigned int u_var = navier_stokes_system.variable_number ("u"); const unsigned int v_var = navier_stokes_system.variable_number ("v"); const unsigned int p_var = navier_stokes_system.variable_number ("p"); const unsigned int alpha_var = navier_stokes_system.variable_number ("alpha"); // Get the Finite Element type for "u". Note this will be // the same as the type for "v". FEType fe_vel_type = navier_stokes_system.variable_type(u_var); // Get the Finite Element type for "p". FEType fe_pres_type = navier_stokes_system.variable_type(p_var); // Build a Finite Element object of the specified type for // the velocity variables. UniquePtr<FEBase> fe_vel (FEBase::build(dim, fe_vel_type)); // Build a Finite Element object of the specified type for // the pressure variables. UniquePtr<FEBase> fe_pres (FEBase::build(dim, fe_pres_type)); // A Gauss quadrature rule for numerical integration. // Let the FEType object decide what order rule is appropriate. QGauss qrule (dim, fe_vel_type.default_quadrature_order()); // Tell the finite element objects to use our quadrature rule. fe_vel->attach_quadrature_rule (&qrule); fe_pres->attach_quadrature_rule (&qrule); // Here we define some references to cell-specific data that // will be used to assemble the linear system. // // The element Jacobian * quadrature weight at each integration point. const std::vector<Real> & JxW = fe_vel->get_JxW(); // The element shape functions evaluated at the quadrature points. const std::vector<std::vector<Real> > & phi = fe_vel->get_phi(); // The element shape function gradients for the velocity // variables evaluated at the quadrature points. const std::vector<std::vector<RealGradient> > & dphi = fe_vel->get_dphi(); // The element shape functions for the pressure variable // evaluated at the quadrature points. const std::vector<std::vector<Real> > & psi = fe_pres->get_phi(); // The value of the linear shape function gradients at the quadrature points // const std::vector<std::vector<RealGradient> > & dpsi = fe_pres->get_dphi(); // A reference to the DofMap object for this system. The DofMap // object handles the index translation from node and element numbers // to degree of freedom numbers. We will talk more about the DofMap // in future examples. const DofMap & dof_map = navier_stokes_system.get_dof_map(); // Define data structures to contain the element matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke" and "Fe". DenseMatrix<Number> Ke; DenseVector<Number> Fe; DenseSubMatrix<Number> Kuu(Ke), Kuv(Ke), Kup(Ke), Kvu(Ke), Kvv(Ke), Kvp(Ke), Kpu(Ke), Kpv(Ke), Kpp(Ke); DenseSubMatrix<Number> Kalpha_p(Ke), Kp_alpha(Ke); DenseSubVector<Number> Fu(Fe), Fv(Fe), Fp(Fe); // 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; std::vector<dof_id_type> dof_indices_u; std::vector<dof_id_type> dof_indices_v; std::vector<dof_id_type> dof_indices_p; std::vector<dof_id_type> dof_indices_alpha; // Find out what the timestep size parameter is from the system, and // the value of theta for the theta method. We use implicit Euler (theta=1) // for this simulation even though it is only first-order accurate in time. // The reason for this decision is that the second-order Crank-Nicolson // method is notoriously oscillatory for problems with discontinuous // initial data such as the lid-driven cavity. Therefore, // we sacrifice accuracy in time for stability, but since the solution // reaches steady state relatively quickly we can afford to take small // timesteps. If you monitor the initial nonlinear residual for this // simulation, you should see that it is monotonically decreasing in time. const Real dt = es.parameters.get<Real>("dt"); // const Real time = es.parameters.get<Real>("time"); const Real theta = 1.; // Now we will loop over all the elements in the mesh that // live on the local processor. We will compute the element // matrix and right-hand-side contribution. Since the mesh // will be refined we want to only consider the ACTIVE elements, // hence we use a variant of the active_elem_iterator. 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); dof_map.dof_indices (elem, dof_indices_u, u_var); dof_map.dof_indices (elem, dof_indices_v, v_var); dof_map.dof_indices (elem, dof_indices_p, p_var); dof_map.dof_indices (elem, dof_indices_alpha, alpha_var); const unsigned int n_dofs = dof_indices.size(); const unsigned int n_u_dofs = dof_indices_u.size(); const unsigned int n_v_dofs = dof_indices_v.size(); const unsigned int n_p_dofs = dof_indices_p.size(); // 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_vel->reinit (elem); fe_pres->reinit (elem); // Zero the element matrix and right-hand side 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 (n_dofs, n_dofs); Fe.resize (n_dofs); // Reposition the submatrices... The idea is this: // // - - - - // | Kuu Kuv Kup | | Fu | // Ke = | Kvu Kvv Kvp |; Fe = | Fv | // | Kpu Kpv Kpp | | Fp | // - - - - // // The DenseSubMatrix.repostition () member takes the // (row_offset, column_offset, row_size, column_size). // // Similarly, the DenseSubVector.reposition () member // takes the (row_offset, row_size) Kuu.reposition (u_var*n_u_dofs, u_var*n_u_dofs, n_u_dofs, n_u_dofs); Kuv.reposition (u_var*n_u_dofs, v_var*n_u_dofs, n_u_dofs, n_v_dofs); Kup.reposition (u_var*n_u_dofs, p_var*n_u_dofs, n_u_dofs, n_p_dofs); Kvu.reposition (v_var*n_v_dofs, u_var*n_v_dofs, n_v_dofs, n_u_dofs); Kvv.reposition (v_var*n_v_dofs, v_var*n_v_dofs, n_v_dofs, n_v_dofs); Kvp.reposition (v_var*n_v_dofs, p_var*n_v_dofs, n_v_dofs, n_p_dofs); Kpu.reposition (p_var*n_u_dofs, u_var*n_u_dofs, n_p_dofs, n_u_dofs); Kpv.reposition (p_var*n_u_dofs, v_var*n_u_dofs, n_p_dofs, n_v_dofs); Kpp.reposition (p_var*n_u_dofs, p_var*n_u_dofs, n_p_dofs, n_p_dofs); // Also, add a row and a column to constrain the pressure Kp_alpha.reposition (p_var*n_u_dofs, p_var*n_u_dofs+n_p_dofs, n_p_dofs, 1); Kalpha_p.reposition (p_var*n_u_dofs+n_p_dofs, p_var*n_u_dofs, 1, n_p_dofs); Fu.reposition (u_var*n_u_dofs, n_u_dofs); Fv.reposition (v_var*n_u_dofs, n_v_dofs); Fp.reposition (p_var*n_u_dofs, n_p_dofs); // Now we will build the element matrix and right-hand-side. // Constructing the RHS requires the solution and its // gradient from the previous timestep. This must be // calculated at each quadrature point by summing the // solution degree-of-freedom values by the appropriate // weight functions. for (unsigned int qp=0; qp<qrule.n_points(); qp++) { // Values to hold the solution & its gradient at the previous timestep. Number u = 0., u_old = 0.; Number v = 0., v_old = 0.; Number p_old = 0.; Gradient grad_u, grad_u_old; Gradient grad_v, grad_v_old; // Compute the velocity & its gradient from the previous timestep // and the old Newton iterate. for (unsigned int l=0; l<n_u_dofs; l++) { // From the old timestep: u_old += phi[l][qp]*navier_stokes_system.old_solution (dof_indices_u[l]); v_old += phi[l][qp]*navier_stokes_system.old_solution (dof_indices_v[l]); grad_u_old.add_scaled (dphi[l][qp], navier_stokes_system.old_solution (dof_indices_u[l])); grad_v_old.add_scaled (dphi[l][qp], navier_stokes_system.old_solution (dof_indices_v[l])); // From the previous Newton iterate: u += phi[l][qp]*navier_stokes_system.current_solution (dof_indices_u[l]); v += phi[l][qp]*navier_stokes_system.current_solution (dof_indices_v[l]); grad_u.add_scaled (dphi[l][qp], navier_stokes_system.current_solution (dof_indices_u[l])); grad_v.add_scaled (dphi[l][qp], navier_stokes_system.current_solution (dof_indices_v[l])); } // Compute the old pressure value at this quadrature point. for (unsigned int l=0; l<n_p_dofs; l++) p_old += psi[l][qp]*navier_stokes_system.old_solution (dof_indices_p[l]); // Definitions for convenience. It is sometimes simpler to do a // dot product if you have the full vector at your disposal. const NumberVectorValue U_old (u_old, v_old); const NumberVectorValue U (u, v); const Number u_x = grad_u(0); const Number u_y = grad_u(1); const Number v_x = grad_v(0); const Number v_y = grad_v(1); // First, an i-loop over the velocity degrees of freedom. // We know that n_u_dofs == n_v_dofs so we can compute contributions // for both at the same time. for (unsigned int i=0; i<n_u_dofs; i++) { Fu(i) += JxW[qp]*(u_old*phi[i][qp] - // mass-matrix term (1.-theta)*dt*(U_old*grad_u_old)*phi[i][qp] + // convection term (1.-theta)*dt*p_old*dphi[i][qp](0) - // pressure term on rhs (1.-theta)*dt*(grad_u_old*dphi[i][qp]) + // diffusion term on rhs theta*dt*(U*grad_u)*phi[i][qp]); // Newton term Fv(i) += JxW[qp]*(v_old*phi[i][qp] - // mass-matrix term (1.-theta)*dt*(U_old*grad_v_old)*phi[i][qp] + // convection term (1.-theta)*dt*p_old*dphi[i][qp](1) - // pressure term on rhs (1.-theta)*dt*(grad_v_old*dphi[i][qp]) + // diffusion term on rhs theta*dt*(U*grad_v)*phi[i][qp]); // Newton term // Note that the Fp block is identically zero unless we are using // some kind of artificial compressibility scheme... // Matrix contributions for the uu and vv couplings. for (unsigned int j=0; j<n_u_dofs; j++) { Kuu(i,j) += JxW[qp]*(phi[i][qp]*phi[j][qp] + // mass matrix term theta*dt*(dphi[i][qp]*dphi[j][qp]) + // diffusion term theta*dt*(U*dphi[j][qp])*phi[i][qp] + // convection term theta*dt*u_x*phi[i][qp]*phi[j][qp]); // Newton term Kuv(i,j) += JxW[qp]*theta*dt*u_y*phi[i][qp]*phi[j][qp]; // Newton term Kvv(i,j) += JxW[qp]*(phi[i][qp]*phi[j][qp] + // mass matrix term theta*dt*(dphi[i][qp]*dphi[j][qp]) + // diffusion term theta*dt*(U*dphi[j][qp])*phi[i][qp] + // convection term theta*dt*v_y*phi[i][qp]*phi[j][qp]); // Newton term Kvu(i,j) += JxW[qp]*theta*dt*v_x*phi[i][qp]*phi[j][qp]; // Newton term } // Matrix contributions for the up and vp couplings. for (unsigned int j=0; j<n_p_dofs; j++) { Kup(i,j) += JxW[qp]*(-theta*dt*psi[j][qp]*dphi[i][qp](0)); Kvp(i,j) += JxW[qp]*(-theta*dt*psi[j][qp]*dphi[i][qp](1)); } } // Now an i-loop over the pressure degrees of freedom. This code computes // the matrix entries due to the continuity equation. Note: To maintain a // symmetric matrix, we may (or may not) multiply the continuity equation by // negative one. Here we do not. for (unsigned int i=0; i<n_p_dofs; i++) { Kp_alpha(i,0) += JxW[qp]*psi[i][qp]; Kalpha_p(0,i) += JxW[qp]*psi[i][qp]; for (unsigned int j=0; j<n_u_dofs; j++) { Kpu(i,j) += JxW[qp]*psi[i][qp]*dphi[j][qp](0); Kpv(i,j) += JxW[qp]*psi[i][qp]*dphi[j][qp](1); } } } // end of the quadrature point qp-loop // At this point the interior element integration has // been completed. However, we have not yet addressed // boundary conditions. For this example we will only // consider simple Dirichlet boundary conditions imposed // via the penalty method. The penalty method used here // is equivalent (for Lagrange basis functions) to lumping // the matrix resulting from the L2 projection penalty // approach introduced in example 3. { // The penalty value. \f$ \frac{1}{\epsilon} \f$ const Real penalty = 1.e10; // 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 s=0; s<elem->n_sides(); s++) if (elem->neighbor(s) == libmesh_nullptr) { UniquePtr<Elem> side (elem->build_side(s)); // Loop over the nodes on the side. for (unsigned int ns=0; ns<side->n_nodes(); ns++) { // Boundary ids are set internally by // build_square(). // 0=bottom // 1=right // 2=top // 3=left // Set u = 1 on the top boundary, 0 everywhere else const Real u_value = (mesh.get_boundary_info().has_boundary_id(elem, s, 2)) ? 1. : 0.; // Set v = 0 everywhere const Real v_value = 0.; // Find the node on the element matching this node on // the side. That defined where in the element matrix // the boundary condition will be applied. for (unsigned int n=0; n<elem->n_nodes(); n++) if (elem->node_id(n) == side->node_id(ns)) { // Matrix contribution. Kuu(n,n) += penalty; Kvv(n,n) += penalty; // Right-hand-side contribution. Fu(n) += penalty*u_value; Fv(n) += penalty*v_value; } } // end face node loop } // end if (elem->neighbor(side) == libmesh_nullptr) } // end boundary condition section // If this assembly program were to be used on an adaptive mesh, // we would have to apply any hanging node constraint equations dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // The element matrix and right-hand-side are now built // for this element. Add them to the global matrix and // right-hand-side vector. The SparseMatrix::add_matrix() // and NumericVector::add_vector() members do this for us. navier_stokes_system.matrix->add_matrix (Ke, dof_indices); navier_stokes_system.rhs->add_vector (Fe, dof_indices); } // end of element loop // We can set the mean of the pressure by setting Falpha navier_stokes_system.rhs->add(navier_stokes_system.rhs->size()-1, 10.); }
void assemble_structure (EquationSystems& es, const std::string& system_name) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Structure"); // Get a constant reference to the mesh object. const MeshBase& mesh = es.get_mesh(); // The dimension that we are running const uint dim = mesh.mesh_dimension(); // Get a reference to the Convection-Diffusion system object. LinearImplicitSystem & system = es.get_system<LinearImplicitSystem> ("Structure"); // Numeric ids corresponding to each variable in the system const uint dx_var = system.variable_number ("dx"); const uint dy_var = system.variable_number ("dy"); const uint dz_var = system.variable_number ("dz"); // Get the Finite Element type for "u". Note this will be // the same as the type for "v". FEType fe_d_type = system.variable_type(dx_var); // Build a Finite Element object of the specified type for // the velocity variables. AutoPtr<FEBase> fe_d (FEBase::build(dim, fe_d_type)); // A Gauss quadrature rule for numerical integration. // Let the \p FEType object decide what order rule is appropriate. QGauss qrule (dim, fe_d_type.default_quadrature_order()); // Tell the finite element objects to use our quadrature rule. fe_d->attach_quadrature_rule (&qrule); // Here we define some references to cell-specific data that // will be used to assemble the linear system. // // The element Jacobian * quadrature weight at each integration point. const std::vector<Real>& JxW = fe_d->get_JxW(); // The element shape function gradients for the velocity // variables evaluated at the quadrature points. const std::vector<std::vector<Real> >& phi = fe_d->get_phi(); const std::vector<std::vector<RealGradient> >& dphi = fe_d->get_dphi(); // 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(); // Define data structures to contain the element matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke" and "Fe". DenseMatrix<Number> Ke; DenseVector<Number> Fe; DenseSubMatrix<Number> Kdxdx(Ke), Kdxdy(Ke), Kdxdz(Ke), Kdydx(Ke), Kdydy(Ke), Kdydz(Ke), Kdzdx(Ke), Kdzdy(Ke), Kdzdz(Ke); DenseSubVector<Number> Fdx(Fe), Fdy(Fe), Fdz(Fe); // 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; std::vector<dof_id_type> dof_indices_dx; std::vector<dof_id_type> dof_indices_dy; std::vector<dof_id_type> dof_indices_dz; // Now we will loop over all the elements in the mesh that // live on the local processor. We will compute the element // matrix and right-hand-side contribution. In case users later // modify this program to include refinement, we will be safe and // will only consider the active elements; hence we use a variant of // the \p active_elem_iterator. 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); dof_map.dof_indices (elem, dof_indices_dx, dx_var); dof_map.dof_indices (elem, dof_indices_dy, dy_var); dof_map.dof_indices (elem, dof_indices_dz, dz_var); const uint n_dofs = dof_indices.size(); const uint n_dx_dofs = dof_indices_dx.size(); const uint n_dy_dofs = dof_indices_dy.size(); const uint n_dz_dofs = dof_indices_dz.size(); // 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_d->reinit (elem); // Zero the element matrix and right-hand side 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 (n_dofs, n_dofs); Fe.resize (n_dofs); // Reposition the submatrices... The idea is this: // // - - - - // | Kuu Kuv Kup | | Fu | // Ke = | Kvu Kvv Kvp |; Fe = | Fv | // | Kpu Kpv Kpp | | Fp | // - - - - // // The \p DenseSubMatrix.repostition () member takes the // (row_offset, column_offset, row_size, column_size). // // Similarly, the \p DenseSubVector.reposition () member // takes the (row_offset, row_size) Kdxdx.reposition (dx_var*n_dx_dofs, dx_var*n_dx_dofs, n_dx_dofs, n_dx_dofs); Kdxdy.reposition (dx_var*n_dx_dofs, dy_var*n_dx_dofs, n_dx_dofs, n_dy_dofs); Kdxdz.reposition (dx_var*n_dx_dofs, dz_var*n_dx_dofs, n_dx_dofs, n_dz_dofs); Kdydx.reposition (dy_var*n_dy_dofs, dx_var*n_dy_dofs, n_dy_dofs, n_dx_dofs); Kdydy.reposition (dy_var*n_dy_dofs, dy_var*n_dy_dofs, n_dy_dofs, n_dy_dofs); Kdydz.reposition (dy_var*n_dy_dofs, dz_var*n_dy_dofs, n_dy_dofs, n_dz_dofs); Kdzdx.reposition (dz_var*n_dy_dofs, dx_var*n_dz_dofs, n_dz_dofs, n_dx_dofs); Kdzdy.reposition (dz_var*n_dy_dofs, dy_var*n_dz_dofs, n_dz_dofs, n_dy_dofs); Kdzdz.reposition (dz_var*n_dy_dofs, dz_var*n_dz_dofs, n_dz_dofs, n_dz_dofs); Fdx.reposition (dx_var*n_dx_dofs, n_dx_dofs); Fdy.reposition (dy_var*n_dx_dofs, n_dy_dofs); Fdz.reposition (dz_var*n_dx_dofs, n_dz_dofs); // Now we will build the element matrix. for (uint qp=0; qp<qrule.n_points(); qp++) { for (uint i=0; i<n_dx_dofs; i++) { Fdx(i) += JxW[qp]*f_x*phi[i][qp]; for (uint j=0; j<n_dx_dofs; j++) { Kdxdx(i,j) += JxW[qp]*( mu*( 2.*dphi[i][qp](0)*dphi[j][qp](0) + dphi[i][qp](1)*dphi[j][qp](1) + dphi[i][qp](2)*dphi[j][qp](2) ) + lmbda*dphi[i][qp](0)*dphi[j][qp](0) ); } for (uint j=0; j<n_dy_dofs; j++) { Kdxdy(i,j) += JxW[qp]*( mu*dphi[i][qp](1)*dphi[j][qp](0) + lmbda*dphi[i][qp](0)*dphi[j][qp](1) ); } for (uint j=0; j<n_dz_dofs; j++) { Kdxdz(i,j) += JxW[qp]*( mu*dphi[i][qp](2)*dphi[j][qp](0) + lmbda*dphi[i][qp](0)*dphi[j][qp](2) ); } } for (uint i=0; i<n_dy_dofs; i++) { Fdy(i) += JxW[qp]*f_y*phi[i][qp]; for (uint j=0; j<n_dx_dofs; j++) { Kdydx(i,j) += JxW[qp]*( mu*dphi[i][qp](0)*dphi[j][qp](1) + lmbda*dphi[i][qp](1)*dphi[j][qp](0) ); } for (uint j=0; j<n_dy_dofs; j++) { Kdydy(i,j) += JxW[qp]*( mu*( dphi[i][qp](0)*dphi[j][qp](0) + 2.*dphi[i][qp](1)*dphi[j][qp](1) + dphi[i][qp](2)*dphi[j][qp](2) ) + lmbda*dphi[i][qp](1)*dphi[j][qp](1) ); } for (uint j=0; j<n_dz_dofs; j++) { Kdydz(i,j) += JxW[qp]*( mu*dphi[i][qp](2)*dphi[j][qp](1) + lmbda*dphi[i][qp](1)*dphi[j][qp](2) ); } } for (uint i=0; i<n_dz_dofs; i++) { Fdz(i) += JxW[qp]*f_z*phi[i][qp]; for (uint j=0; j<n_dx_dofs; j++) { Kdzdx(i,j) += JxW[qp]*( mu*dphi[i][qp](0)*dphi[j][qp](2) + lmbda*dphi[i][qp](2)*dphi[j][qp](0) ); } for (uint j=0; j<n_dy_dofs; j++) { Kdzdy(i,j) += JxW[qp]*( mu*dphi[i][qp](1)*dphi[j][qp](2) + lmbda*dphi[i][qp](2)*dphi[j][qp](1) ); } for (uint j=0; j<n_dz_dofs; j++) { Kdzdz(i,j) += JxW[qp]*( mu*( dphi[i][qp](0)*dphi[j][qp](0) + dphi[i][qp](1)*dphi[j][qp](1) + 2.*dphi[i][qp](2)*dphi[j][qp](2) ) + lmbda*dphi[i][qp](2)*dphi[j][qp](2) ); } } } // end of the quadrature point qp-loop // If this assembly program were to be used on an adaptive mesh, // we would have to apply any hanging node constraint equations. dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // The element matrix and right-hand-side are now built // for this element. Add them to the global matrix and // right-hand-side vector. The \p NumericMatrix::add_matrix() // and \p NumericVector::add_vector() members do this for us. system.matrix->add_matrix (Ke, dof_indices); system.rhs->add_vector (Fe, dof_indices); } // end of element loop // system.matrix->close(); // system.matrix->print(); // system.rhs->close(); // system.rhs->print(); // That's it. return; }