ex15.c
来自「一个用来实现偏微分方程中网格的计算库」· C语言 代码 · 共 831 行 · 第 1/2 页
C
831 行
{ // Second derivatives to be returned. Tensor hessu; // x and y coordinates in space const Real x = p(0); const Real y = p(1); hessu(0,0) = 256.*2.*(1-6.*x+6.*x*x)*(y-y*y)*(y-y*y); hessu(0,1) = 256.*4.*(x-x*x)*(1.-2.*x)*(y-y*y)*(1.-2.*y); hessu(1,1) = 256.*2.*(x-x*x)*(x-x*x)*(1.-6.*y+6.*y*y); // Hessians are always symmetric hessu(1,0) = hessu(0,1); return hessu;}Number forcing_function_2D(const Point& p){ // x and y coordinates in space const Real x = p(0); const Real y = p(1); // Equals laplacian(laplacian(u)) return 256. * 8. * (3.*((y-y*y)*(y-y*y)+(x-x*x)*(x-x*x)) + (1.-6.*x+6.*x*x)*(1.-6.*y+6.*y*y));}Number exact_3D_solution(const Point& p, const Parameters&, // parameters, not needed const std::string&, // sys_name, not needed const std::string&) // unk_name, not needed{ // xyz coordinates in space const Real x = p(0); const Real y = p(1); const Real z = p(2); // analytic solution value return 4096.*(x-x*x)*(x-x*x)*(y-y*y)*(y-y*y)*(z-z*z)*(z-z*z);}Gradient exact_3D_derivative(const Point& p, const Parameters&, // parameters, not needed const std::string&, // sys_name, not needed const std::string&) // unk_name, not needed{ // First derivatives to be returned. Gradient gradu; // xyz coordinates in space const Real x = p(0); const Real y = p(1); const Real z = p(2); gradu(0) = 4096.*2.*(x-x*x)*(1.-2.*x)*(y-y*y)*(y-y*y)*(z-z*z)*(z-z*z); gradu(1) = 4096.*2.*(x-x*x)*(x-x*x)*(y-y*y)*(1.-2.*y)*(z-z*z)*(z-z*z); gradu(2) = 4096.*2.*(x-x*x)*(x-x*x)*(y-y*y)*(y-y*y)*(z-z*z)*(1.-2.*z); return gradu;}// We now define the hessian of the exact solutionTensor exact_3D_hessian(const Point& p, const Parameters&, // parameters, not needed const std::string&, // sys_name, not needed const std::string&) // unk_name, not needed{ // Second derivatives to be returned. Tensor hessu; // xyz coordinates in space const Real x = p(0); const Real y = p(1); const Real z = p(2); hessu(0,0) = 4096.*(2.-12.*x+12.*x*x)*(y-y*y)*(y-y*y)*(z-z*z)*(z-z*z); hessu(0,1) = 4096.*4.*(x-x*x)*(1.-2.*x)*(y-y*y)*(1.-2.*y)*(z-z*z)*(z-z*z); hessu(0,2) = 4096.*4.*(x-x*x)*(1.-2.*x)*(y-y*y)*(y-y*y)*(z-z*z)*(1.-2.*z); hessu(1,1) = 4096.*(x-x*x)*(x-x*x)*(2.-12.*y+12.*y*y)*(z-z*z)*(z-z*z); hessu(1,2) = 4096.*4.*(x-x*x)*(x-x*x)*(y-y*y)*(1.-2.*y)*(z-z*z)*(1.-2.*z); hessu(2,2) = 4096.*(x-x*x)*(x-x*x)*(y-y*y)*(y-y*y)*(2.-12.*z+12.*z*z); // Hessians are always symmetric hessu(1,0) = hessu(0,1); hessu(2,0) = hessu(0,2); hessu(2,1) = hessu(1,2); return hessu;}Number forcing_function_3D(const Point& p){ // xyz coordinates in space const Real x = p(0); const Real y = p(1); const Real z = p(2); // Equals laplacian(laplacian(u)) return 4096. * 8. * (3.*((y-y*y)*(y-y*y)*(x-x*x)*(x-x*x) + (z-z*z)*(z-z*z)*(x-x*x)*(x-x*x) + (z-z*z)*(z-z*z)*(y-y*y)*(y-y*y)) + (1.-6.*x+6.*x*x)*(1.-6.*y+6.*y*y)*(z-z*z)*(z-z*z) + (1.-6.*x+6.*x*x)*(1.-6.*z+6.*z*z)*(y-y*y)*(y-y*y) + (1.-6.*y+6.*y*y)*(1.-6.*z+6.*z*z)*(x-x*x)*(x-x*x));}// 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 ENABLE_AMR#ifdef ENABLE_SECOND_DERIVATIVES // It is a good idea to make sure we are assembling // the proper system. libmesh_assert (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 PetscMatrix::add_matrix() // and \p PetscVector::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 ENABLE_SECOND_DERIVATIVES#endif // #ifdef ENABLE_AMR}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?