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 CSFSolver::compute_system_matrices(const Vector<double> &last_relax_step, const Vector<double> &previous_time_step, SparseMatrix<double> &mass_output, SparseMatrix<double> &stiffness_output) { mass_output = 0; stiffness_output = 0; const QGauss<2> quadrature_formula(3); FEValues<2> fe_values(fe, quadrature_formula, update_values | update_gradients | update_JxW_values | update_quadrature_points); const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); FullMatrix<double> local_mass_matrix(dofs_per_cell, dofs_per_cell); FullMatrix<double> local_stiffness_matrix(dofs_per_cell, dofs_per_cell); std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell); std::vector<Tensor<1, 2, double>> last_relax_grads(n_q_points); std::vector<Tensor<1, 2, double>> previous_step_grads(n_q_points); for(auto cell : dof_handler.active_cell_iterators()) { local_mass_matrix = 0; local_stiffness_matrix = 0; fe_values.reinit(cell); fe_values.get_function_gradients(last_relax_step, last_relax_grads); fe_values.get_function_gradients(previous_time_step, previous_step_grads); for(unsigned int q_pt_idx = 0; q_pt_idx < n_q_points; q_pt_idx++) { for(unsigned int i = 0; i < dofs_per_cell; i++) { for(unsigned int j = 0; j < dofs_per_cell; j++) { // Compute the shared denominator - normalizing to avoid infinities double grad_sum = last_relax_grads[q_pt_idx].norm() + previous_step_grads[q_pt_idx].norm(); double grad_sum_inv = 1.0 / MAX(grad_sum, grad_epsilon); // If there is a weight function, compute its value at the quad point double weight_fn_value = weight_function(fe_values.quadrature_point(q_pt_idx)); // Compute the mass component local_mass_matrix(i, j) += (fe_values.shape_value(i, q_pt_idx) * fe_values.shape_value(j, q_pt_idx) * weight_fn_value * grad_sum_inv * fe_values.JxW(q_pt_idx)); // Compute the stiffness component local_stiffness_matrix(i, j) += (fe_values.shape_grad(i, q_pt_idx) * fe_values.shape_grad(j, q_pt_idx) * weight_fn_value * grad_sum_inv * fe_values.JxW(q_pt_idx)); } } } // Copy the local matrices into the global output cell->get_dof_indices(local_dof_indices); for(unsigned int i = 0; i < dofs_per_cell; i++) { for(unsigned int j = 0; j < dofs_per_cell; j++) { mass_output.add(local_dof_indices[i], local_dof_indices[j], local_mass_matrix(i, j)); stiffness_output.add(local_dof_indices[i], local_dof_indices[j], local_stiffness_matrix(i, j)); } } } }
void compute_stresses(EquationSystems & es) { LOG_SCOPE("compute_stresses()", "main"); const MeshBase & mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); ElasticityRBConstruction & system = es.get_system<ElasticityRBConstruction>("RBElasticity"); unsigned int displacement_vars[3]; displacement_vars[0] = system.variable_number ("u"); displacement_vars[1] = system.variable_number ("v"); displacement_vars[2] = system.variable_number ("w"); const unsigned int u_var = system.variable_number ("u"); const DofMap & dof_map = system.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<RealGradient> > & dphi = fe->get_dphi(); // Also, get a reference to the ExplicitSystem ExplicitSystem & stress_system = es.get_system<ExplicitSystem>("StressSystem"); const DofMap & stress_dof_map = stress_system.get_dof_map(); unsigned int sigma_vars[3][3]; sigma_vars[0][0] = stress_system.variable_number ("sigma_00"); sigma_vars[0][1] = stress_system.variable_number ("sigma_01"); sigma_vars[0][2] = stress_system.variable_number ("sigma_02"); sigma_vars[1][0] = stress_system.variable_number ("sigma_10"); sigma_vars[1][1] = stress_system.variable_number ("sigma_11"); sigma_vars[1][2] = stress_system.variable_number ("sigma_12"); sigma_vars[2][0] = stress_system.variable_number ("sigma_20"); sigma_vars[2][1] = stress_system.variable_number ("sigma_21"); sigma_vars[2][2] = stress_system.variable_number ("sigma_22"); unsigned int vonMises_var = stress_system.variable_number ("vonMises"); // Storage for the stress dof indices on each element std::vector<std::vector<dof_id_type> > dof_indices_var(system.n_vars()); std::vector<dof_id_type> stress_dof_indices_var; // To store the stress tensor on each element DenseMatrix<Number> elem_sigma; 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; for (unsigned int var=0; var<3; var++) dof_map.dof_indices (elem, dof_indices_var[var], displacement_vars[var]); fe->reinit (elem); elem_sigma.resize(3, 3); for (unsigned int qp=0; qp<qrule.n_points(); qp++) for (unsigned int C_i=0; C_i<3; C_i++) for (unsigned int C_j=0; C_j<3; C_j++) for (unsigned int C_k=0; C_k<3; C_k++) { const unsigned int n_var_dofs = dof_indices_var[C_k].size(); // Get the gradient at this quadrature point Gradient displacement_gradient; for (unsigned int l=0; l<n_var_dofs; l++) displacement_gradient.add_scaled(dphi[l][qp], system.current_solution(dof_indices_var[C_k][l])); for (unsigned int C_l=0; C_l<3; C_l++) elem_sigma(C_i,C_j) += JxW[qp] * (elasticity_tensor(C_i, C_j, C_k, C_l) * displacement_gradient(C_l)); } // Get the average stresses by dividing by the element volume elem_sigma.scale(1./elem->volume()); // load elem_sigma data into stress_system for (unsigned int i=0; i<3; i++) for (unsigned int j=0; j<3; j++) { stress_dof_map.dof_indices (elem, stress_dof_indices_var, sigma_vars[i][j]); // We are using CONSTANT MONOMIAL basis functions, hence we only need to get // one dof index per variable dof_id_type dof_index = stress_dof_indices_var[0]; if ((stress_system.solution->first_local_index() <= dof_index) && (dof_index < stress_system.solution->last_local_index())) { stress_system.solution->set(dof_index, elem_sigma(i,j)); } } // Also, the von Mises stress Number vonMises_value = std::sqrt(0.5*(pow(elem_sigma(0,0) - elem_sigma(1,1),2.) + pow(elem_sigma(1,1) - elem_sigma(2,2),2.) + pow(elem_sigma(2,2) - elem_sigma(0,0),2.) + 6.*(pow(elem_sigma(0,1),2.) + pow(elem_sigma(1,2),2.) + pow(elem_sigma(2,0),2.)) )); stress_dof_map.dof_indices (elem, stress_dof_indices_var, vonMises_var); dof_id_type dof_index = stress_dof_indices_var[0]; if ((stress_system.solution->first_local_index() <= dof_index) && (dof_index < stress_system.solution->last_local_index())) { stress_system.solution->set(dof_index, vonMises_value); } } // Should call close and update when we set vector entries directly stress_system.solution->close(); stress_system.update(); }
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; }
// 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 & 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, "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 ("vel_x"); const unsigned int v_var = navier_stokes_system.variable_number ("vel_y"); const unsigned int p_var = navier_stokes_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 = 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. std::unique_ptr<FEBase> fe_vel (FEBase::build(dim, fe_vel_type)); // Build a Finite Element object of the specified type for // the pressure variables. std::unique_ptr<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); 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; // 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 theta = 1.; // The kinematic viscosity, multiplies the "viscous" terms. const Real nu = es.parameters.get<Real>("nu"); // The system knows whether or not we need to do a pressure pin. // This is only required for problems with all-Dirichlet boundary // conditions on the velocity. const bool pin_pressure = es.parameters.get<bool>("pin_pressure"); // 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. 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); 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 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); 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 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*nu*(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*nu*(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*nu*(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*nu*(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++) 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. We now need to pin the pressure to zero at global // node number "pressure_node". This effectively removes the // non-trivial null space of constant pressure solutions. The // pressure pin is not necessary in problems that have "outflow" // BCs, like Poiseuille flow with natural BCs. In fact it is // actually wrong to do so, since the pressure is not // under-specified in that situation. if (pin_pressure) { const Real penalty = 1.e10; const unsigned int pressure_node = 0; const Real p_value = 0.0; for (auto c : elem->node_index_range()) if (elem->node_id(c) == pressure_node) { Kpp(c,c) += penalty; Fp(c) += penalty*p_value; } } // Since we're using heterogeneous DirichletBoundary objects for // the boundary conditions, we need to call a specific function // to constrain the element stiffness matrix. 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 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 }
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 }
// Here we define the matrix assembly routine for // the Helmholtz system. This function will be // called to form the stiffness matrix and right-hand side. void assemble_helmholtz(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, "Helmholtz"); // Get a constant reference to the mesh object. const MeshBase & mesh = es.get_mesh(); // The dimension that we are in const unsigned int dim = mesh.mesh_dimension(); // Get a reference to our system, as before FrequencySystem & f_system = es.get_system<FrequencySystem> (system_name); // A const 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 = f_system.get_dof_map(); // 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); // For the admittance boundary condition, // get the fluid density const Real rho = es.parameters.get<Real>("rho"); // In here, we will add the element matrices to the // <i>additional</i> matrices "stiffness_mass", "damping", // and the additional vector "rhs", not to the members // "matrix" and "rhs". Therefore, get writable // references to them SparseMatrix<Number> & stiffness = f_system.get_matrix("stiffness"); SparseMatrix<Number> & damping = f_system.get_matrix("damping"); SparseMatrix<Number> & mass = f_system.get_matrix("mass"); NumericVector<Number> & freq_indep_rhs = f_system.get_vector("rhs"); // Some solver packages (PETSc) are especially picky about // allocating sparsity structure and truly assigning values // to this structure. Namely, matrix additions, as performed // later, exhibit acceptable performance only for identical // sparsity structures. Therefore, explicitly zero the // values in the collective matrix, so that matrix additions // encounter identical sparsity structures. SparseMatrix<Number> & matrix = *f_system.matrix; // ------------------------------------------------------------------ // Finite Element related stuff // // Build a Finite Element object of the specified type. Since the // FEBase::build() member dynamically creates memory we will // store the object as a 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); // 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(); // Now this is slightly different from example 4. // We will not add directly to the global matrix, // but to the additional matrices "stiffness_mass" and "damping". // The same holds for the right-hand-side vector Fe, which we will // later on store in the additional vector "rhs". // The zero_matrix is used to explicitly induce the same sparsity // structure in the overall matrix. // see later on. (At least) the mass, and stiffness matrices, however, // are inherently real. Therefore, store these as one complex // matrix. This will definitely save memory. DenseMatrix<Number> Ke, Ce, Me, zero_matrix; 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) { // Start logging the element initialization. START_LOG("elem init", "assemble_helmholtz"); // 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 & resize the element matrix and right-hand side before // summing them, with different element types in the mesh this // is quite necessary. { const unsigned int n_dof_indices = dof_indices.size(); Ke.resize (n_dof_indices, n_dof_indices); Ce.resize (n_dof_indices, n_dof_indices); Me.resize (n_dof_indices, n_dof_indices); zero_matrix.resize (n_dof_indices, n_dof_indices); Fe.resize (n_dof_indices); } // Stop logging the element initialization. STOP_LOG("elem init", "assemble_helmholtz"); // Now loop over the quadrature points. This handles // the numeric integration. START_LOG("stiffness & mass", "assemble_helmholtz"); 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). Note the braces on the rhs // of Ke(i,j): these are quite necessary to finally compute // Real*(Point*Point) = Real, and not something else... 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]); Me(i,j) += JxW[qp]*(phi[i][qp]*phi[j][qp]); } } STOP_LOG("stiffness & mass", "assemble_helmholtz"); // Now compute the contribution to the element matrix // (due to mixed boundary conditions) if the current // element lies on the boundary. // // 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) == libmesh_nullptr) { LOG_SCOPE("damping", "assemble_helmholtz"); // 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, SECOND); // Tell the finte element object to use our // quadrature rule. fe_face->attach_quadrature_rule (&qface); // The value of the shape functions at the quadrature // points. const std::vector<std::vector<Real> > & phi_face = fe_face->get_phi(); // The Jacobian// Quadrature Weight at the quadrature // points on the face. const std::vector<Real> & JxW_face = fe_face->get_JxW(); // Compute the shape function values on the element // face. fe_face->reinit(elem, side); // For the Robin BCs consider a normal admittance an=1 // at some parts of the bounfdary const Real an_value = 1.; // Loop over the face quadrature points for integration. for (unsigned int qp=0; qp<qface.n_points(); qp++) { // Element matrix contributrion due to precribed // admittance boundary conditions. for (unsigned int i=0; i<phi_face.size(); i++) for (unsigned int j=0; j<phi_face.size(); j++) Ce(i,j) += rho*an_value*JxW_face[qp]*phi_face[i][qp]*phi_face[j][qp]; } } // If this assembly program were to be used on an adaptive mesh, // we would have to apply any hanging node constraint equations // by uncommenting the following lines: // std::vector<unsigned int> dof_indicesC = dof_indices; // std::vector<unsigned int> dof_indicesM = dof_indices; // dof_map.constrain_element_matrix (Ke, dof_indices); // dof_map.constrain_element_matrix (Ce, dof_indicesC); // dof_map.constrain_element_matrix (Me, dof_indicesM); // Finally, simply add the contributions to the additional // matrices and vector. stiffness.add_matrix (Ke, dof_indices); damping.add_matrix (Ce, dof_indices); mass.add_matrix (Me, dof_indices); // For the overall matrix, explicitly zero the entries where // we added values in the other ones, so that we have // identical sparsity footprints. matrix.add_matrix(zero_matrix, dof_indices); } // loop el // It now remains to compute the rhs. Here, we simply // get the normal velocities values on the boundary from the // mesh data. { LOG_SCOPE("rhs", "assemble_helmholtz"); // get a reference to the mesh data. const MeshData & mesh_data = es.get_mesh_data(); // We will now loop over all nodes. In case nodal data // for a certain node is available in the MeshData, we simply // adopt the corresponding value for the rhs vector. // Note that normal data was given in the mesh data file, // i.e. one value per node libmesh_assert_equal_to (mesh_data.n_val_per_node(), 1); MeshBase::const_node_iterator node_it = mesh.nodes_begin(); const MeshBase::const_node_iterator node_end = mesh.nodes_end(); for ( ; node_it != node_end; ++node_it) { // the current node pointer const Node * node = *node_it; // check if the mesh data has data for the current node // and do for all components if (mesh_data.has_data(node)) for (unsigned int comp=0; comp<node->n_comp(0, 0); comp++) { // the dof number unsigned int dn = node->dof_number(0, 0, comp); // set the nodal value freq_indep_rhs.set(dn, mesh_data.get_data(node)[0]); } } } // All done! }
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; }
/** * Compute the Cauchy stress for the current solution. */ void compute_stresses() { const Real young_modulus = es.parameters.get<Real>("young_modulus"); const Real poisson_ratio = es.parameters.get<Real>("poisson_ratio"); const MeshBase & mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); NonlinearImplicitSystem & system = es.get_system<NonlinearImplicitSystem>("NonlinearElasticity"); unsigned int displacement_vars[3]; displacement_vars[0] = system.variable_number ("u"); displacement_vars[1] = system.variable_number ("v"); displacement_vars[2] = system.variable_number ("w"); const unsigned int u_var = system.variable_number ("u"); const DofMap & dof_map = system.get_dof_map(); FEType fe_type = dof_map.variable_type(u_var); std::unique_ptr<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<RealGradient>> & dphi = fe->get_dphi(); // Also, get a reference to the ExplicitSystem ExplicitSystem & stress_system = es.get_system<ExplicitSystem>("StressSystem"); const DofMap & stress_dof_map = stress_system.get_dof_map(); unsigned int sigma_vars[6]; sigma_vars[0] = stress_system.variable_number ("sigma_00"); sigma_vars[1] = stress_system.variable_number ("sigma_01"); sigma_vars[2] = stress_system.variable_number ("sigma_02"); sigma_vars[3] = stress_system.variable_number ("sigma_11"); sigma_vars[4] = stress_system.variable_number ("sigma_12"); sigma_vars[5] = stress_system.variable_number ("sigma_22"); // Storage for the stress dof indices on each element std::vector<std::vector<dof_id_type>> dof_indices_var(system.n_vars()); std::vector<dof_id_type> stress_dof_indices_var; // To store the stress tensor on each element DenseMatrix<Number> elem_avg_stress_tensor(3, 3); for (const auto & elem : mesh.active_local_element_ptr_range()) { for (unsigned int var=0; var<3; var++) dof_map.dof_indices (elem, dof_indices_var[var], displacement_vars[var]); const unsigned int n_var_dofs = dof_indices_var[0].size(); fe->reinit (elem); // clear the stress tensor elem_avg_stress_tensor.resize(3, 3); for (unsigned int qp=0; qp<qrule.n_points(); qp++) { DenseMatrix<Number> grad_u(3, 3); // Row is variable u1, u2, or u3, column is x, y, or z 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) * system.current_solution(dof_indices_var[var_i][j]); DenseMatrix<Number> strain_tensor(3, 3); for (unsigned int i=0; i<3; i++) for (unsigned int j=0; j<3; j++) { strain_tensor(i,j) += 0.5 * (grad_u(i,j) + grad_u(j,i)); for (unsigned int k=0; k<3; k++) strain_tensor(i,j) += 0.5 * grad_u(k,i)*grad_u(k,j); } // Define the deformation gradient DenseMatrix<Number> F(3, 3); F = grad_u; for (unsigned int var=0; var<3; var++) F(var, var) += 1.; DenseMatrix<Number> stress_tensor(3, 3); 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++) stress_tensor(i,j) += elasticity_tensor(young_modulus, poisson_ratio, i, j, k, l) * strain_tensor(k, l); // stress_tensor now holds the second Piola-Kirchoff stress (PK2) at point qp. // However, in this example we want to compute the Cauchy stress which is given by // 1/det(F) * F * PK2 * F^T, hence we now apply this transformation. stress_tensor.scale(1./F.det()); stress_tensor.left_multiply(F); stress_tensor.right_multiply_transpose(F); // We want to plot the average Cauchy stress on each element, hence // we integrate stress_tensor elem_avg_stress_tensor.add(JxW[qp], stress_tensor); } // Get the average stress per element by dividing by volume elem_avg_stress_tensor.scale(1./elem->volume()); // load elem_sigma data into stress_system unsigned int stress_var_index = 0; for (unsigned int i=0; i<3; i++) for (unsigned int j=i; j<3; j++) { stress_dof_map.dof_indices (elem, stress_dof_indices_var, sigma_vars[stress_var_index]); // We are using CONSTANT MONOMIAL basis functions, hence we only need to get // one dof index per variable dof_id_type dof_index = stress_dof_indices_var[0]; if ((stress_system.solution->first_local_index() <= dof_index) && (dof_index < stress_system.solution->last_local_index())) stress_system.solution->set(dof_index, elem_avg_stress_tensor(i,j)); stress_var_index++; } } // Should call close and update when we set vector entries directly stress_system.solution->close(); stress_system.update(); }
// 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 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]; } } // This will zero off-diagonal entries of Ke corresponding to // Dirichlet dofs. dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // We want the diagonal of constrained dofs to be zero too for (unsigned int local_dof_index=0; local_dof_index<n_dofs; local_dof_index++) { dof_id_type global_dof_index = dof_indices[local_dof_index]; if (dof_map.is_constrained_dof(global_dof_index)) { Ke(local_dof_index, local_dof_index) = 0.; } } A_matrix->add_matrix (Ke, dof_indices); F_vector->add_vector (Fe, dof_indices); } A_matrix->close(); F_vector->close(); }
// This function assembles the system matrix and right-hand-side // for 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"); // 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(); // Copy the speed of sound and fluid density // to a local variable. const Real speed = es.parameters.get<Real>("speed"); const Real rho = es.parameters.get<Real>("fluid density"); // Get a reference to our system, as before. NewmarkSystem & t_system = es.get_system<NewmarkSystem> (system_name); // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. FEType fe_type = t_system.get_dof_map().variable_type(0); // In here, we will add the element matrices to the // @e additional matrices "stiffness_mass" and "damping" // and the additional vector "force", not to the members // "matrix" and "rhs". Therefore, get writable // references to them. SparseMatrix<Number> & stiffness = t_system.get_matrix("stiffness"); SparseMatrix<Number> & damping = t_system.get_matrix("damping"); SparseMatrix<Number> & mass = t_system.get_matrix("mass"); NumericVector<Number> & force = t_system.get_vector("force"); // Some solver packages (PETSc) are especially picky about // allocating sparsity structure and truly assigning values // to this structure. Namely, matrix additions, as performed // later, exhibit acceptable performance only for identical // sparsity structures. Therefore, explicitly zero the // values in the collective matrix, so that matrix additions // encounter identical sparsity structures. SparseMatrix<Number> & matrix = *t_system.matrix; DenseMatrix<Number> zero_matrix; // 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 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); // 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 = t_system.get_dof_map(); // The element mass, damping and stiffness matrices // and the element contribution to the rhs. DenseMatrix<Number> Ke, Ce, 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); // 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 and rhs 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 HEX8 // and now have a PRISM6). { const unsigned int n_dof_indices = dof_indices.size(); Ke.resize (n_dof_indices, n_dof_indices); Ce.resize (n_dof_indices, n_dof_indices); Me.resize (n_dof_indices, n_dof_indices); zero_matrix.resize (n_dof_indices, n_dof_indices); Fe.resize (n_dof_indices); } // 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]); Me(i,j) += JxW[qp]*phi[i][qp]*phi[j][qp] *1./(speed*speed); } // end of the matrix summation loop } // end of quadrature point loop // Now compute the contribution to the element matrix and the // right-hand-side vector if the current element lies on the // boundary. { // In this example no natural boundary conditions will // be considered. The code is left here so it can easily // be extended. // // don't do this for any side for (unsigned int side=0; side<elem->n_sides(); side++) if (!true) // if (elem->neighbor(side) == libmesh_nullptr) { // 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, SECOND); // Tell the finte element object to use our // quadrature rule. fe_face->attach_quadrature_rule (&qface); // The value of the shape functions at the quadrature // points. const std::vector<std::vector<Real> > & phi_face = fe_face->get_phi(); // The Jacobian * Quadrature Weight at the quadrature // points on the face. const std::vector<Real> & JxW_face = fe_face->get_JxW(); // Compute the shape function values on the element // face. fe_face->reinit(elem, side); // Here we consider a normal acceleration acc_n=1 applied to // the whole boundary of our mesh. const Real acc_n_value = 1.0; // Loop over the face quadrature points for integration. for (unsigned int qp=0; qp<qface.n_points(); qp++) { // Right-hand-side contribution due to prescribed // normal acceleration. for (unsigned int i=0; i<phi_face.size(); i++) { Fe(i) += acc_n_value*rho *phi_face[i][qp]*JxW_face[qp]; } } // end face quadrature point loop } // end if (elem->neighbor(side) == libmesh_nullptr) // In this example the Dirichlet boundary conditions will be // imposed via panalty method after the // system is assembled. } // 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 // by uncommenting the following lines: // std::vector<unsigned int> dof_indicesC = dof_indices; // std::vector<unsigned int> dof_indicesM = dof_indices; // dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices); // dof_map.constrain_element_matrix (Ce, dof_indicesC); // dof_map.constrain_element_matrix (Me, dof_indicesM); // Finally, simply add the contributions to the additional // matrices and vector. stiffness.add_matrix (Ke, dof_indices); damping.add_matrix (Ce, dof_indices); mass.add_matrix (Me, dof_indices); force.add_vector (Fe, dof_indices); // For the overall matrix, explicitly zero the entries where // we added values in the other ones, so that we have // identical sparsity footprints. matrix.add_matrix(zero_matrix, dof_indices); } // end of element loop }
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 Real forcing_magnitude = es.parameters.get<Real>("forcing_magnitude"); 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<Real> >& phi = fe->get_phi(); const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi(); const std::vector<Real>& JxW_face = fe_face->get_JxW(); const std::vector<std::vector<Real> >& phi_face = fe_face->get_phi(); const std::vector<Point>& face_normals = fe_face->get_normals(); const std::vector<Point>& face_xyz = fe_face->get_xyz(); const std::vector<std::vector<Real> >& phi_neighbor_face = fe_neighbor_face->get_phi(); // 1. Move mesh_clone based on soln. // 2. Compute and store all contact forces. // 3. Augment the sparsity pattern. { UniquePtr<MeshBase> mesh_clone = mesh.clone(); move_mesh(*mesh_clone, soln); _augment_sparsity.clear_contact_element_map(); clear_contact_data(); MeshBase::const_element_iterator el = mesh_clone->active_elements_begin(); const MeshBase::const_element_iterator end_el = mesh_clone->active_elements_end(); for ( ; el != end_el; ++el) { const Elem* elem = *el; for (unsigned int side=0; side<elem->n_sides(); side++) { if (elem->neighbor(side) == NULL) { bool on_lower_contact_surface = mesh_clone->get_boundary_info().has_boundary_id (elem, side, CONTACT_BOUNDARY_LOWER); bool on_upper_contact_surface = mesh_clone->get_boundary_info().has_boundary_id (elem, side, CONTACT_BOUNDARY_UPPER); if( on_lower_contact_surface && on_upper_contact_surface ) { libmesh_error_msg("Should not be on both surfaces at the same time"); } if( on_lower_contact_surface || on_upper_contact_surface ) { fe_face->reinit(elem, side); // Let's stash xyz and normals because we reinit on other_elem below std::vector<Point> face_normals_stashed = fe_face->get_normals(); std::vector<Point> face_xyz_stashed = fe_face->get_xyz(); for (unsigned int qp=0; qp<qface.n_points(); qp++) { Point line_point = face_xyz_stashed[qp]; Point line_direction = face_normals_stashed[qp]; // find an element which the line intersects, based on the plane // defined by the normal at the centroid of the other element bool found_other_elem = false; // Note that here we loop over all elements (not just local elements) // to be sure we find the appropriate other element. MeshBase::const_element_iterator other_el = mesh_clone->active_elements_begin(); const MeshBase::const_element_iterator other_end_el = mesh_clone->active_elements_end(); for ( ; other_el != other_end_el; ++other_el) { const Elem* other_elem = *other_el; if( other_elem->close_to_point(line_point, _contact_proximity_tol) ) { for (unsigned int other_side=0; other_side<other_elem->n_sides(); other_side++) if (other_elem->neighbor(other_side) == NULL) { boundary_id_type other_surface_id = on_lower_contact_surface ? CONTACT_BOUNDARY_UPPER : CONTACT_BOUNDARY_LOWER; if( mesh_clone->get_boundary_info().has_boundary_id (other_elem, other_side, other_surface_id) ) { UniquePtr<Elem> other_side_elem = other_elem->build_side(other_side); // Define a plane based on the normal at the centroid of other_side_elem // and check where line_point + s * line_direction (s \in R) intersects // this plane. const Point reference_centroid ( FEInterface::inverse_map ( other_elem->dim(), _sys.get_dof_map().variable_type(0), other_elem, other_side_elem->centroid())); std::vector<Point> reference_centroid_vector; reference_centroid_vector.push_back(reference_centroid); fe_face->reinit( other_elem, other_side, TOLERANCE, &reference_centroid_vector); Point plane_normal = face_normals[0]; Point plane_point = face_xyz[0]; // line_direction.dot(plane_normal) == 0.0 if the line and plane are // parallel. Ignore this case since it should give zero contact force. if( (line_direction * plane_normal) != 0. ) { // The signed distance between the line and the plane Real signed_distance = ( (plane_point - line_point) * plane_normal) / (line_direction * plane_normal); Point intersection_point = line_point + signed_distance * line_direction; // Note that signed_distance = (intersection_point - line_point) dot line_direction // since line_direction is a unit vector. if(other_side_elem->close_to_point(intersection_point, _contains_point_tol)) { // If the signed distance is negative then we have overlapping elements // i.e. we have detected contact. if(signed_distance < 0.0) { // We need to store the intersection point, and the element/side // that it belongs to. We can use this to calculate the contact // force later on. std::vector<Point> intersection_point_vec; intersection_point_vec.push_back(intersection_point); std::vector<Point> inverse_intersection_point_vec; FEInterface::inverse_map( other_elem->dim(), fe->get_fe_type(), other_elem, intersection_point_vec, inverse_intersection_point_vec); IntersectionPointData contact_intersection_pt_data( other_elem->id(), other_side, intersection_point, inverse_intersection_point_vec[0], line_point, line_direction); set_contact_data( elem->id(), side, qp, contact_intersection_pt_data); // We also need to keep track of which elements // are coupled in order to augment the sparsity pattern // appropriately later on. _augment_sparsity.add_contact_element( elem->id(), other_elem->id()); } // If we've found an element that contains // intersection_point then we're done for // the current quadrature point hence break // out to the next qp. found_other_elem = true; break; } } } } } if(found_other_elem) { break; } } // end for other_el } // end for qp } // end if on_contact_surface } // end if nieghbor(side_) != NULL } // end for side } // end for el } // Clear the Jacobian matrix and reinitialize it so that // we get the updated sparsity pattern if(jacobian) { dof_map.clear_sparsity(); dof_map.compute_sparsity(mesh); #ifdef LIBMESH_HAVE_PETSC PetscMatrix<Number>* petsc_jacobian = cast_ptr<PetscMatrix<Number>*>(jacobian); petsc_jacobian->update_preallocation_and_zero(); #else libmesh_error(); #endif } 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)} }; DenseMatrix<Number> Ke_en; DenseSubMatrix<Number> Ke_var_en[3][3] = { {DenseSubMatrix<Number>(Ke_en), DenseSubMatrix<Number>(Ke_en), DenseSubMatrix<Number>(Ke_en)}, {DenseSubMatrix<Number>(Ke_en), DenseSubMatrix<Number>(Ke_en), DenseSubMatrix<Number>(Ke_en)}, {DenseSubMatrix<Number>(Ke_en), DenseSubMatrix<Number>(Ke_en), DenseSubMatrix<Number>(Ke_en)} }; 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++) { 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++) { // Row is variable u, v, or w column is x, y, or z 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); } } if( (elem->subdomain_id() == TOP_SUBDOMAIN) ) { // assemble \int_Omega f_i v_i \dx DenseVector<Number> f_vec(3); f_vec(0) = forcing_magnitude/10.; f_vec(1) = 0.0; f_vec(2) = -forcing_magnitude; for (unsigned int dof_i=0; dof_i<n_var_dofs; dof_i++) { for(unsigned int i=0; i<3; i++) { Re_var[i](dof_i) += JxW[qp] * ( f_vec(i) * phi[dof_i][qp] ); } } } // 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); } } } // Add contribution due to contact penalty forces for (unsigned int side=0; side<elem->n_sides(); side++) if (elem->neighbor(side) == NULL) { bool on_lower_contact_surface = mesh.get_boundary_info().has_boundary_id (elem, side, CONTACT_BOUNDARY_LOWER); bool on_upper_contact_surface = mesh.get_boundary_info().has_boundary_id (elem, side, CONTACT_BOUNDARY_UPPER); if( on_lower_contact_surface && on_upper_contact_surface ) { libmesh_error_msg("Should not be on both surfaces at the same time"); } if( on_lower_contact_surface || on_upper_contact_surface ) { fe_face->reinit(elem, side); for (unsigned int qp=0; qp<qface.n_points(); qp++) { bool contact_detected = is_contact_detected( elem->id(), side, qp); if(contact_detected) { IntersectionPointData intersection_pt_data = get_contact_data( elem->id(), side, qp); Point intersection_point = intersection_pt_data._intersection_point; Point line_point = intersection_pt_data._line_point; Point line_direction = intersection_pt_data._line_direction; // signed_distance = (intersection_point - line_point) dot line_direction, // hence we use this to get the contact force Real contact_force = get_contact_penalty() * ( (intersection_point - line_point) * line_direction ); for (unsigned int dof_i=0; dof_i<n_var_dofs; dof_i++) { for(unsigned int i=0; i<3; i++) { Re_var[i](dof_i) += JxW_face[qp] * ( contact_force * face_normals[qp](i) * phi_face[dof_i][qp] ); } } // Differentiate contact_force wrt solution coefficients to get // the Jacobian entries. // // Note that intersection_point and line_point // are linear functions of the solution. // // Also, line_direction is a function of the solution, but let's // neglect that for now to make it easier to get the Jacobian. // This is probably fine anyway, since this approximation will // have negligible effect as we approach convergence. // dofs local to this element, due to differentiating line_point 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++) { Ke_var[i][j](dof_i,dof_j) += JxW_face[qp] * ( get_contact_penalty() * (-phi_face[dof_j][qp] * line_direction(j)) * face_normals[qp](i) * phi_face[dof_i][qp] ); } } // contribution due to dofs on the remote element, due to // differentiating intersection_point { dof_id_type neighbor_elem_id = intersection_pt_data._neighbor_element_id; const Elem* contact_neighbor = mesh.elem(neighbor_elem_id); unsigned char neighbor_side_index = intersection_pt_data._neighbor_side_index; std::vector<Point> inverse_intersection_point_vec; inverse_intersection_point_vec.push_back( intersection_pt_data._inverse_mapped_intersection_point); fe_neighbor_face->reinit( contact_neighbor, neighbor_side_index, TOLERANCE, &inverse_intersection_point_vec); std::vector<dof_id_type> neighbor_dof_indices; std::vector< std::vector<unsigned int> > neighbor_dof_indices_var(3); dof_map.dof_indices (contact_neighbor, neighbor_dof_indices); for(unsigned int var=0; var<3; var++) { dof_map.dof_indices (contact_neighbor, neighbor_dof_indices_var[var], var); } const unsigned int n_neighbor_dofs = neighbor_dof_indices.size(); const unsigned int n_neighbor_var_dofs = neighbor_dof_indices_var[0].size(); Ke_en.resize (n_dofs,n_neighbor_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_en[var_i][var_j].reposition( var_i*n_var_dofs, var_j*n_neighbor_var_dofs, n_var_dofs, n_neighbor_var_dofs); } for (unsigned int dof_i=0; dof_i<n_var_dofs; dof_i++) for (unsigned int dof_j=0; dof_j<n_neighbor_var_dofs; dof_j++) { for(unsigned int i=0; i<3; i++) for(unsigned int j=0; j<3; j++) { Ke_var_en[i][j](dof_i,dof_j) += JxW_face[qp] * ( get_contact_penalty() * (phi_neighbor_face[dof_j][0] * line_direction(j)) * face_normals[qp](i) * phi_face[dof_i][qp] ); } } if(jacobian) { jacobian->add_matrix(Ke_en,dof_indices,neighbor_dof_indices); } } } } } } 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); } } }
void assemble_elasticity(EquationSystems & es, const std::string & system_name) { libmesh_assert_equal_to (system_name, "Elasticity"); const MeshBase & mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); Real pival = libMesh::pi; Real sigma = 1000.0; Real omega = 2.0*pival*27e6; Real Mu0 = 4e-7*pival; char *filename = "coordinates_custom.dat"; 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<Real>> & phi = fe->get_phi(); const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi(); const std::vector<Point> & q_point = fe->get_xyz(); 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); const Real eps = 1.e-3; for (unsigned int qp=0; qp<qrule.n_points(); qp++) { const Real x = q_point[qp](0); const Real y = q_point[qp](1); Real fxyz = 0.0; Real fxy = 0.0; 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] + phi[i][qp]*phi[j][qp]/x/x)*x ; } if ( x<=0.3 && y>=0.0 && y<=1.0 ) { Real Kfactor = Mu0*sigma*omega; for (unsigned int i=0; i<n_u_dofs; i++) for (unsigned int j=0; j<n_v_dofs; j++) { Kuv(i,j) += (-1.0)*(JxW[qp]*(phi[i][qp]*phi[j][qp])*Kfactor*x); } for (unsigned int i=0; i<n_v_dofs; i++) for (unsigned int j=0; j<n_u_dofs; j++) { Kvu(i,j) += JxW[qp]*(phi[i][qp]*phi[j][qp])*Kfactor*x ; } fxyz = -Kfactor*solev(x,y,filename)*Mu0*omega/2.0/pival; } 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] + phi[i][qp]*phi[j][qp]/x/x )*x ; } // Real fxyz = exact_solution(x,y)*pival*pival/2.0 + pival*0.5*sin(0.5*pival*x)*sin(0.5*pival*y)/x + exact_solution(x,y)/x/x + Kfactor*exact_solution2(x,y); // Real fxy = exact_solution2(x,y)*pival*pival/2.0 - pival*0.5*cos(0.5*pival*x)*cos(0.5*pival*y)/x + exact_solution2(x,y)/x/x + Kfactor*exact_solution(x,y); for (unsigned int i=0; i<n_u_dofs; i++) Fu(i) += JxW[qp]*fxyz*phi[i][qp]*x; for (unsigned int i = 0;i<n_v_dofs;i++) Fv(i) += JxW[qp]*fxy*phi[i][qp]*x; } for (unsigned int s=0; s<elem->n_sides(); s++) if (elem->neighbor(s) == libmesh_nullptr) { const std::vector<std::vector<Real> > & phi_face = fe_face->get_phi(); const std::vector<Real> & JxW_face = fe_face->get_JxW(); const std::vector<Point> & qface_point = fe_face->get_xyz(); const std::vector<Point>& qface_normals = fe_face->get_normals(); fe_face->reinit(elem, s); UniquePtr<Elem> side (elem->build_side(s)); /* 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]*xf; // 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]*xf; }*/ double check = 0; { for (unsigned int ns=0; ns<side->n_nodes(); ns++) { const Real penalty = 1.e10; for (unsigned int n=0; n<elem->n_nodes(); n++) if (elem->node(n) == side->node(ns)) { Node *node = elem->get_node(n); Point poi = *node; const Real xf = poi(0); const Real yf = poi(1); for(unsigned int j=0; j<n_u_dofs; ++j) Kuu(n,j) = 0.; for(unsigned int j=0; j<n_v_dofs; ++j) Kvv(n,j) = 0.; Kuu(n,n) = 1.; Kvv(n,n) = 1.; Fu(n) = 0.0; Fv(n) = 0.0; // Fu(n) = exact_solution(xf,yf); // Fv(n) = exact_solution2(xf,yf); } } } } 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_elasticity(EquationSystems& es, const std::string& 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 unsigned int lambda_var = system.variable_number ("lambda"); const DofMap& dof_map = system.get_dof_map(); FEType fe_type = dof_map.variable_type(0); AutoPtr<FEBase> fe (FEBase::build(dim, fe_type)); QGauss qrule (dim, fe_type.default_quadrature_order()); fe->attach_quadrature_rule (&qrule); AutoPtr<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); DenseSubMatrix<Number> Klambda_v(Ke), Kv_lambda(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; std::vector<dof_id_type> dof_indices_lambda; 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); dof_map.dof_indices (elem, dof_indices_lambda, lambda_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_lambda_dofs = dof_indices_lambda.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); // Also, add a row and a column to enforce the constraint Kv_lambda.reposition (v_var*n_u_dofs, v_var*n_u_dofs+n_v_dofs, n_v_dofs, 1); Klambda_v.reposition (v_var*n_v_dofs+n_v_dofs, v_var*n_v_dofs, 1, 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(side) == NULL) { const std::vector<boundary_id_type> bc_ids = mesh.boundary_info->boundary_ids (elem,side); 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); for (std::vector<boundary_id_type>::const_iterator b = bc_ids.begin(); b != bc_ids.end(); ++b) { const boundary_id_type bc_id = *b; for (unsigned int qp=0; qp<qface.n_points(); qp++) { // Add the loading if( bc_id == 2 ) { for (unsigned int i=0; i<n_v_dofs; i++) { Fv(i) += JxW_face[qp]* (-1.) * phi_face[i][qp]; } } // Add the constraint contributions if( bc_id == 1 ) { for (unsigned int i=0; i<n_v_dofs; i++) for (unsigned int j=0; j<n_lambda_dofs; j++) { Kv_lambda(i,j) += JxW_face[qp]* (-1.) * phi_face[i][qp]; } for (unsigned int i=0; i<n_lambda_dofs; i++) for (unsigned int j=0; j<n_v_dofs; j++) { Klambda_v(i,j) += JxW_face[qp]* (-1.) * phi_face[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 Assembly::reinitNeighborAtReference(const Elem * neighbor, const std::vector<Point> & reference_points) { unsigned int neighbor_dim = neighbor->dim(); // reinit neighbor element for (std::map<FEType, FEBase *>::iterator it = _fe_neighbor[neighbor_dim].begin(); it != _fe_neighbor[neighbor_dim].end(); ++it) { FEBase * fe_neighbor = it->second; FEType fe_type = it->first; FEShapeData * fesd = _fe_shape_data_face_neighbor[fe_type]; it->second->reinit(neighbor, &reference_points); _current_fe_neighbor[it->first] = it->second; fesd->_phi.shallowCopy(const_cast<std::vector<std::vector<Real> > &>(fe_neighbor->get_phi())); fesd->_grad_phi.shallowCopy(const_cast<std::vector<std::vector<RealGradient> > &>(fe_neighbor->get_dphi())); if (_need_second_derivative[fe_type]) fesd->_second_phi.shallowCopy(const_cast<std::vector<std::vector<RealTensor> > &>(fe_neighbor->get_d2phi())); } ArbitraryQuadrature * neighbor_rule = _holder_qrule_neighbor[neighbor_dim]; neighbor_rule->setPoints(reference_points); setNeighborQRule(neighbor_rule, neighbor_dim); _current_neighbor_elem = neighbor; // Calculate the volume of the neighbor FEType fe_type (neighbor->default_order() , LAGRANGE); AutoPtr<FEBase> fe (FEBase::build(neighbor->dim(), fe_type)); const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<Point> & q_points = fe->get_xyz(); // The default quadrature rule should integrate the mass matrix, // thus it should be plenty to compute the area QGauss qrule (neighbor->dim(), fe_type.default_quadrature_order()); fe->attach_quadrature_rule(&qrule); fe->reinit(neighbor); // set the coord transformation MooseArray<Real> coord; coord.resize(qrule.n_points()); switch (_coord_type) // coord type should be the same for the neighbor { case Moose::COORD_XYZ: for (unsigned int qp = 0; qp < qrule.n_points(); qp++) coord[qp] = 1.; break; case Moose::COORD_RZ: for (unsigned int qp = 0; qp < qrule.n_points(); qp++) coord[qp] = 2 * M_PI * q_points[qp](0); break; case Moose::COORD_RSPHERICAL: for (unsigned int qp = 0; qp < qrule.n_points(); qp++) coord[qp] = 4 * M_PI * q_points[qp](0) * q_points[qp](0); break; default: mooseError("Unknown coordinate system"); break; } _current_neighbor_volume = 0.; for (unsigned int qp = 0; qp < qrule.n_points(); qp++) _current_neighbor_volume += JxW[qp] * coord[qp]; coord.release(); }
// 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 LinearElasticityWithContact::compute_stresses() { 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(); unsigned int displacement_vars[3]; displacement_vars[0] = _sys.variable_number ("u"); displacement_vars[1] = _sys.variable_number ("v"); displacement_vars[2] = _sys.variable_number ("w"); 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<RealGradient> > & dphi = fe->get_dphi(); // Also, get a reference to the ExplicitSystem ExplicitSystem & stress_system = es.get_system<ExplicitSystem>("StressSystem"); const DofMap & stress_dof_map = stress_system.get_dof_map(); unsigned int sigma_vars[6]; sigma_vars[0] = stress_system.variable_number ("sigma_00"); sigma_vars[1] = stress_system.variable_number ("sigma_01"); sigma_vars[2] = stress_system.variable_number ("sigma_02"); sigma_vars[3] = stress_system.variable_number ("sigma_11"); sigma_vars[4] = stress_system.variable_number ("sigma_12"); sigma_vars[5] = stress_system.variable_number ("sigma_22"); unsigned int vonMises_var = stress_system.variable_number ("vonMises"); // Storage for the stress dof indices on each element std::vector< std::vector<dof_id_type> > dof_indices_var(_sys.n_vars()); std::vector<dof_id_type> stress_dof_indices_var; // To store the stress tensor on each element DenseMatrix<Number> elem_avg_stress_tensor(3, 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; for (unsigned int var=0; var<3; var++) dof_map.dof_indices (elem, dof_indices_var[var], displacement_vars[var]); const unsigned int n_var_dofs = dof_indices_var[0].size(); fe->reinit (elem); // clear the stress tensor elem_avg_stress_tensor.resize(3, 3); for (unsigned int qp=0; qp<qrule.n_points(); qp++) { // Row is variable u1, u2, or u3, 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) * _sys.current_solution(dof_indices_var[var_i][j]); DenseMatrix<Number> stress_tensor(3, 3); 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++) stress_tensor(i,j) += elasticity_tensor(young_modulus, poisson_ratio, i, j, k, l) * grad_u(k,l); // We want to plot the average stress on each element, hence // we integrate stress_tensor elem_avg_stress_tensor.add(JxW[qp], stress_tensor); } // Get the average stress per element by dividing by volume elem_avg_stress_tensor.scale(1./elem->volume()); // load elem_sigma data into stress_system unsigned int stress_var_index = 0; for (unsigned int i=0; i<3; i++) for (unsigned int j=i; j<3; j++) { stress_dof_map.dof_indices (elem, stress_dof_indices_var, sigma_vars[stress_var_index]); // We are using CONSTANT MONOMIAL basis functions, hence we only need to get // one dof index per variable dof_id_type dof_index = stress_dof_indices_var[0]; if ((stress_system.solution->first_local_index() <= dof_index) && (dof_index < stress_system.solution->last_local_index())) stress_system.solution->set(dof_index, elem_avg_stress_tensor(i,j)); stress_var_index++; } // Also, the von Mises stress Number vonMises_value = std::sqrt(0.5*(pow(elem_avg_stress_tensor(0,0) - elem_avg_stress_tensor(1,1), 2.) + pow(elem_avg_stress_tensor(1,1) - elem_avg_stress_tensor(2,2), 2.) + pow(elem_avg_stress_tensor(2,2) - elem_avg_stress_tensor(0,0), 2.) + 6.*(pow(elem_avg_stress_tensor(0,1), 2.) + pow(elem_avg_stress_tensor(1,2), 2.) + pow(elem_avg_stress_tensor(2,0), 2.)))); stress_dof_map.dof_indices (elem, stress_dof_indices_var, vonMises_var); dof_id_type dof_index = stress_dof_indices_var[0]; if ((stress_system.solution->first_local_index() <= dof_index) && (dof_index < stress_system.solution->last_local_index())) stress_system.solution->set(dof_index, vonMises_value); } // Should call close and update when we set vector entries directly stress_system.solution->close(); stress_system.update(); }
/** * Evaluate the residual of the nonlinear system. */ virtual void residual (const NumericVector<Number> & soln, NumericVector<Number> & residual, NonlinearImplicitSystem & /*sys*/) { const Real young_modulus = es.parameters.get<Real>("young_modulus"); const Real poisson_ratio = es.parameters.get<Real>("poisson_ratio"); const Real forcing_magnitude = es.parameters.get<Real>("forcing_magnitude"); const MeshBase & mesh = es.get_mesh(); const unsigned int dim = mesh.mesh_dimension(); NonlinearImplicitSystem & system = es.get_system<NonlinearImplicitSystem>("NonlinearElasticity"); const unsigned int u_var = system.variable_number ("u"); const DofMap & dof_map = system.get_dof_map(); FEType fe_type = dof_map.variable_type(u_var); std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type)); QGauss qrule (dim, fe_type.default_quadrature_order()); fe->attach_quadrature_rule (&qrule); std::unique_ptr<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<Real>> & phi = fe->get_phi(); const std::vector<std::vector<RealGradient>> & dphi = fe->get_dphi(); DenseVector<Number> Re; DenseSubVector<Number> Re_var[3] = {DenseSubVector<Number>(Re), DenseSubVector<Number>(Re), DenseSubVector<Number>(Re)}; std::vector<dof_id_type> dof_indices; std::vector<std::vector<dof_id_type>> dof_indices_var(3); residual.zero(); for (const auto & elem : mesh.active_local_element_ptr_range()) { 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); for (unsigned int qp=0; qp<qrule.n_points(); qp++) { DenseVector<Number> u_vec(3); DenseMatrix<Number> grad_u(3, 3); for (unsigned int var_i=0; var_i<3; var_i++) { for (unsigned int j=0; j<n_var_dofs; j++) u_vec(var_i) += phi[j][qp]*soln(dof_indices_var[var_i][j]); // Row is variable u, v, or w column is x, y, or z 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]); } DenseMatrix<Number> strain_tensor(3, 3); for (unsigned int i=0; i<3; i++) for (unsigned int j=0; j<3; j++) { strain_tensor(i,j) += 0.5 * (grad_u(i,j) + grad_u(j,i)); for (unsigned int k=0; k<3; k++) strain_tensor(i,j) += 0.5 * grad_u(k,i)*grad_u(k,j); } // Define the deformation gradient DenseMatrix<Number> F(3, 3); F = grad_u; for (unsigned int var=0; var<3; var++) F(var, var) += 1.; DenseMatrix<Number> stress_tensor(3, 3); 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++) stress_tensor(i,j) += elasticity_tensor(young_modulus, poisson_ratio, i, j, k, l) * strain_tensor(k,l); DenseVector<Number> f_vec(3); f_vec(0) = 0.; f_vec(1) = 0.; f_vec(2) = -forcing_magnitude; 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++) { Number FxStress_ij = 0.; for (unsigned int m=0; m<3; m++) FxStress_ij += F(i,m) * stress_tensor(m,j); Re_var[i](dof_i) += JxW[qp] * (-FxStress_ij * dphi[dof_i][qp](j)); } Re_var[i](dof_i) += JxW[qp] * (f_vec(i) * phi[dof_i][qp]); } } dof_map.constrain_element_vector (Re, dof_indices); residual.add_vector (Re, dof_indices); } }
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.); } } } }
// Here we compute the residual R(x) = K(x)*x - f. The current solution // x is passed in the soln vector void compute_residual (const NumericVector<Number>& soln, NumericVector<Number>& residual, NonlinearImplicitSystem& sys) { EquationSystems &es = *_equation_system; // Get a constant reference to the mesh object. const MeshBase& mesh = es.get_mesh(); // The dimension that we are running const unsigned int dim = mesh.mesh_dimension(); libmesh_assert_equal_to (dim, 2); // Get a reference to the NonlinearImplicitSystem we are solving NonlinearImplicitSystem& system = es.get_system<NonlinearImplicitSystem>("Laplace-Young"); // A reference to the \p DofMap object for this system. The \p DofMap // object handles the index translation from node and element numbers // to degree of freedom numbers. We will talk more about the \p DofMap // in future examples. const DofMap& dof_map = system.get_dof_map(); // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. FEType fe_type = dof_map.variable_type(0); // Build a Finite Element object of the specified type. Since the // \p FEBase::build() member dynamically creates memory we will // store the object as an \p AutoPtr<FEBase>. This can be thought // of as a pointer that will clean up after itself. AutoPtr<FEBase> fe (FEBase::build(dim, fe_type)); // A 5th order Gauss quadrature rule for numerical integration. QGauss qrule (dim, FIFTH); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (&qrule); // Declare a special finite element object for // boundary integration. AutoPtr<FEBase> fe_face (FEBase::build(dim, fe_type)); // Boundary integration requires one quadraure rule, // with dimensionality one less than the dimensionality // of the element. QGauss qface(dim-1, FIFTH); // Tell the finte element object to use our // quadrature rule. fe_face->attach_quadrature_rule (&qface); // Here we define some references to cell-specific data that // will be used to assemble the linear system. // We begin with the element Jacobian * quadrature weight at each // integration point. const std::vector<Real>& JxW = fe->get_JxW(); // The element shape functions evaluated at the quadrature points. const std::vector<std::vector<Real> >& phi = fe->get_phi(); // The element shape function gradients evaluated at the quadrature // points. const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi(); // Define data structures to contain the resdual contributions DenseVector<Number> Re; // This vector will hold the degree of freedom indices for // the element. These define where in the global system // the element degrees of freedom get mapped. std::vector<unsigned int> dof_indices; // Now we will loop over all the active elements in the mesh which // are local to this processor. // We will compute the element residual. residual.zero(); MeshBase::const_element_iterator el = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end(); for ( ; el != end_el; ++el) { // Store a pointer to the element we are currently // working on. This allows for nicer syntax later. const Elem* elem = *el; // Get the degree of freedom indices for the // current element. These define where in the global // matrix and right-hand-side this element will // contribute to. dof_map.dof_indices (elem, dof_indices); // Compute the element-specific data for the current // element. This involves computing the location of the // quadrature points (q_point) and the shape functions // (phi, dphi) for the current element. fe->reinit (elem); // We use the resize member here because // the number of degrees of freedom might have changed from // the last element. Note that this will be the case if the // element type is different (i.e. the last element was a // triangle, now we are on a quadrilateral). Re.resize (dof_indices.size()); // Now we will build the residual. This involves // the construction of the matrix K and multiplication of it // with the current solution x. We rearrange this into two loops: // In the first, we calculate only the contribution of // K_ij*x_j which is independent of the row i. In the second loops, // we multiply with the row-dependent part and add it to the element // residual. for (unsigned int qp=0; qp<qrule.n_points(); qp++) { Number u = 0; Gradient grad_u; for (unsigned int j=0; j<phi.size(); j++) { u += phi[j][qp]*soln(dof_indices[j]); grad_u += dphi[j][qp]*soln(dof_indices[j]); } const Number K = 1./std::sqrt(1. + grad_u*grad_u); for (unsigned int i=0; i<phi.size(); i++) Re(i) += JxW[qp]*( K*(dphi[i][qp]*grad_u) + kappa*phi[i][qp]*u ); } // At this point the interior element integration has // been completed. However, we have not yet addressed // boundary conditions. // The following loops over the sides of the element. // If the element has no neighbor on a side then that // side MUST live on a boundary of the domain. for (unsigned int side=0; side<elem->n_sides(); side++) if (elem->neighbor(side) == NULL) { // The value of the shape functions at the quadrature // points. const std::vector<std::vector<Real> >& phi_face = fe_face->get_phi(); // The Jacobian * Quadrature Weight at the quadrature // points on the face. const std::vector<Real>& JxW_face = fe_face->get_JxW(); // Compute the shape function values on the element face. fe_face->reinit(elem, side); // Loop over the face quadrature points for integration. for (unsigned int qp=0; qp<qface.n_points(); qp++) { // This is the right-hand-side contribution (f), // which has to be subtracted from the current residual for (unsigned int i=0; i<phi_face.size(); i++) Re(i) -= JxW_face[qp]*sigma*phi_face[i][qp]; } } dof_map.constrain_element_vector (Re, dof_indices); residual.add_vector (Re, dof_indices); } // That's it. }
// 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? }
// This function computes the Jacobian K(x) void compute_jacobian (const NumericVector<Number>& soln, SparseMatrix<Number>& jacobian, NonlinearImplicitSystem& sys) { // Get a reference to the equation system. EquationSystems &es = *_equation_system; // Get a constant reference to the mesh object. const MeshBase& mesh = es.get_mesh(); // The dimension that we are running const unsigned int dim = mesh.mesh_dimension(); // Get a reference to the NonlinearImplicitSystem we are solving NonlinearImplicitSystem& system = es.get_system<NonlinearImplicitSystem>("Laplace-Young"); // A reference to the \p DofMap object for this system. The \p DofMap // object handles the index translation from node and element numbers // to degree of freedom numbers. We will talk more about the \p DofMap // in future examples. const DofMap& dof_map = system.get_dof_map(); // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. FEType fe_type = dof_map.variable_type(0); // Build a Finite Element object of the specified type. Since the // \p FEBase::build() member dynamically creates memory we will // store the object as an \p AutoPtr<FEBase>. This can be thought // of as a pointer that will clean up after itself. AutoPtr<FEBase> fe (FEBase::build(dim, fe_type)); // A 5th order Gauss quadrature rule for numerical integration. QGauss qrule (dim, FIFTH); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (&qrule); // Here we define some references to cell-specific data that // will be used to assemble the linear system. // We begin with the element Jacobian * quadrature weight at each // integration point. const std::vector<Real>& JxW = fe->get_JxW(); // The element shape functions evaluated at the quadrature points. const std::vector<std::vector<Real> >& phi = fe->get_phi(); // The element shape function gradients evaluated at the quadrature // points. const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi(); // Define data structures to contain the Jacobian element matrix. // Following basic finite element terminology we will denote these // "Ke". More detail is in example 3. DenseMatrix<Number> Ke; // This vector will hold the degree of freedom indices for // the element. These define where in the global system // the element degrees of freedom get mapped. std::vector<unsigned int> dof_indices; // Now we will loop over all the active elements in the mesh which // are local to this processor. // We will compute the element Jacobian contribution. MeshBase::const_element_iterator el = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end(); for ( ; el != end_el; ++el) { // Store a pointer to the element we are currently // working on. This allows for nicer syntax later. const Elem* elem = *el; // Get the degree of freedom indices for the // current element. These define where in the global // matrix and right-hand-side this element will // contribute to. dof_map.dof_indices (elem, dof_indices); // Compute the element-specific data for the current // element. This involves computing the location of the // quadrature points (q_point) and the shape functions // (phi, dphi) for the current element. fe->reinit (elem); // Zero the element Jacobian before // summing them. We use the resize member here because // the number of degrees of freedom might have changed from // the last element. Note that this will be the case if the // element type is different (i.e. the last element was a // triangle, now we are on a quadrilateral). Ke.resize (dof_indices.size(), dof_indices.size()); // Now we will build the element Jacobian. This involves // a double loop to integrate the test funcions (i) against // the trial functions (j). Note that the Jacobian depends // on the current solution x, which we access using the soln // vector. // for (unsigned int qp=0; qp<qrule.n_points(); qp++) { Gradient grad_u; for (unsigned int i=0; i<phi.size(); i++) grad_u += dphi[i][qp]*soln(dof_indices[i]); const Number K = 1./std::sqrt(1. + grad_u*grad_u); for (unsigned int i=0; i<phi.size(); i++) for (unsigned int j=0; j<phi.size(); j++) Ke(i,j) += JxW[qp]*( K*(dphi[i][qp]*dphi[j][qp]) + kappa*phi[i][qp]*phi[j][qp] ); } dof_map.constrain_element_matrix (Ke, dof_indices); // Add the element matrix to the system Jacobian. jacobian.add_matrix (Ke, dof_indices); } // That's it. }
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); } }
// Stiffness and RHS assembly // Equation references are from Samanta and Zabaras, 2005 void EnergySystem :: assemble(){ // GENERAL VARIABLES // Get a constant reference to the mesh object. const MeshBase& mesh = this->get_mesh(); // The dimension that we are running const unsigned int dim = this->ndim(); // FEM THERMODYNAMIC RELATIONSHIPS (ThermoEq Class) // Determine the FEM type (should be same for all ThermoEq variables) FEType fe_type_thermo = thermo->variable_type(0); // Build FE object; accessed via a pointer AutoPtr<FEBase> fe_thermo(FEBase::build(dim, fe_type_thermo)); // Setup a quadrature rule QGauss qrule_thermo(dim, fe_type_thermo.default_quadrature_order()); // Link FE and Quadrature fe_thermo->attach_quadrature_rule(&qrule_thermo); // References to shape functions and derivatives const vector<std::vector<Real> >& N_thermo = fe_thermo->get_phi(); const vector<std::vector<RealGradient> >& B_thermo = fe_thermo->get_dphi(); // Setup a DOF map const DofMap& dof_map_thermo = thermo->get_dof_map(); // FEM MOMENTUM EQUATION // Determine the FEM type FEType fe_type_momentum = momentum->variable_type(0); // Build FE object; accessed via a pointer AutoPtr<FEBase> fe_momentum(FEBase::build(dim, fe_type_momentum)); // Setup a quadrature rule QGauss qrule_momentum(dim, fe_type_momentum.default_quadrature_order()); // Link FE and Quadrature fe_momentum->attach_quadrature_rule(&qrule_momentum); // References to shape functions and derivatives const vector<std::vector<Real> >& N_momentum = fe_momentum->get_phi(); // Setup a DOF map const DofMap& dof_map_momentum = momentum->get_dof_map(); // FEM ENERGY EQ. RELATIONSHIPS // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. FEType fe_type = this->variable_type(0); // Build a Finite Element object of the specified type 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 vector<Real>& JxW = fe->get_JxW(); const vector<Real>& JxW_face = fe_face->get_JxW(); // The element shape functions evaluated at the quadrature points. const vector<std::vector<Real> >& N = fe->get_phi(); const vector<std::vector<Real> >& N_face = fe_face->get_phi(); // Element shape function gradients evaluated at quadrature points const vector<std::vector<RealGradient> >& B = fe->get_dphi(); // The XY locations of the quadrature points used for face integration const vector<Point>& qface_points = fe_face->get_xyz(); // A reference to the \p DofMap objects const DofMap& dof_map = this->get_dof_map(); // this system // DEFINE VECTOR AND MATRIX VARIABLES // Define data structures to contain the element matrix // and right-hand-side vector contribution (Eq. 107) DenseMatrix<Number> Me; // [\hat{M} + \hat{M}_{\delta}] DenseMatrix<Number> Ne; // [\hat{N} + \hat{N}_{\delta}] DenseMatrix<Number> Ke; // [\hat{K} + \hat{K}_{\delta}] DenseVector<Number> Fe; // [\hat{F} + \hat{F}_{\delta}] //DenseVector<Number> Fe_old; // element force vector (previous time) DenseVector<Number> h; // element enthalpy vector (previous time) DenseVector<Number> h_dot; //DenseVector<Number> delta_h_dot; DenseMatrix<Number> Mstar; // general time integration stiffness matrix (Eq. 125) DenseVector<Number> R; // general time integration force vector (Eq. 126) // Storage vectors for the degree of freedom indices std::vector<unsigned int> dof_indices; // this system (h) // std::vector<unsigned int> dof_indices_hdot; // std::vector<unsigned int> dof_indices_deltahdot; std::vector<unsigned int> dof_indices_velocity; // this system std::vector<unsigned int> dof_indices_rho; // ThermoEq density std::vector<unsigned int> dof_indices_tmp; // ThermoEq temperature std::vector<unsigned int> dof_indices_f; // ThermoEq liquid fraction std::vector<unsigned int> dof_indices_eps; // ThermoEq epsilon // Define the necessary constants const Number gamma = get_constant<Number>("gamma"); const Number dt = get_constant<Number>("dt"); // time step Real time = this->time; // current time const Number ks = thermo->get_constant<Number>("conductivity_solid"); const Number kf = thermo->get_constant<Number>("conductivity_fluid"); const Number cs = thermo->get_constant<Number>("specific_heat_solid"); const Number cf = thermo->get_constant<Number>("specific_heat_fluid"); const Number Te = thermo->get_constant<Number>("eutectic_temperature"); const Number hf = thermo->get_constant<Number>("latent_heat"); // Index of density variable in ThermoEq system const unsigned int rho_idx = thermo->variable_number("density"); const unsigned int tmp_idx = thermo->variable_number("temperature"); const unsigned int f_idx = thermo->variable_number("liquid_mass_fraction"); const unsigned int eps_idx = thermo->variable_number("epsilon"); // Loop over all the elements in the mesh that are on local processor 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){ // Pointer to the element current element const Elem* elem = *el; // Get the degree of freedom indices for the current element dof_map.dof_indices(elem, dof_indices, 0); //dof_map.dof_indices(elem, dof_indices_hdot, 1); //dof_map.dof_indices(elem, dof_indices_deltahdot, 2); dof_map_momentum.dof_indices(elem, dof_indices_velocity); dof_map_thermo.dof_indices(elem, dof_indices_rho, rho_idx); dof_map_thermo.dof_indices(elem, dof_indices_tmp, tmp_idx); dof_map_thermo.dof_indices(elem, dof_indices_f, f_idx); dof_map_thermo.dof_indices(elem, dof_indices_eps, eps_idx); // Compute the element-specific data for the current element fe->reinit (elem); fe_thermo->reinit(elem); fe_momentum->reinit(elem); // Zero the element matrices and vectors Me.resize (dof_indices.size(), dof_indices.size()); // [\hat{M} + \hat{M}_{\delta}] Ne.resize (dof_indices.size(), dof_indices.size()); // Ke.resize (dof_indices.size(), dof_indices.size()); Fe.resize (dof_indices.size()); // Extract a vector of quadrature x,y,z coordinates const vector<Point> qp_vec = fe->get_xyz(); // Compute the element length, h Number elem_length = thermo->element_length(elem); // Compute the RHS and mass and stiffness matrix for this element (Me) for (unsigned int qp = 0; qp < qrule.n_points(); qp++){ // Get the velocity vector at this point (old value) VectorValue<Number> v; for (unsigned int i = 0; i < N_momentum.size(); i++){ for (unsigned int j = 0; j < dim; j++){ v(j) += N_momentum[i][qp] * momentum->old_solution(dof_indices_velocity[2*i+j]); } } // Compute ThermoEq variables; must be mapped from node to quadrature points Number T = 0; Gradient grad_T; Number f = 0; Gradient grad_f; Number rho = 0; Number rho_old = 0; Number eps = 0; for (unsigned int i = 0; i < N_thermo.size(); i++){ T += N_thermo[i][qp] * thermo->current_solution(dof_indices_tmp[i]); grad_T.add_scaled(B_thermo[i][qp], thermo->current_solution(dof_indices_tmp[i])); f += N_thermo[i][qp] * thermo->current_solution(dof_indices_f[i]); grad_f.add_scaled(B_thermo[i][qp], thermo->current_solution(dof_indices_f[i])); rho += N_thermo[i][qp] * thermo->current_solution(dof_indices_rho[i]); rho_old += N_thermo[i][qp] * thermo->old_solution(dof_indices_rho[i]); eps += N_thermo[i][qp] * thermo->current_solution(dof_indices_eps[i]); } // Compute EnergySystem variables Gradient grad_h; for (unsigned int i = 0; i < B.size(); i++){ grad_h.add_scaled(B[i][qp], this->current_solution(dof_indices[i])); } // Compute T_{,k}^h v_k^h and f_{,k} v_k^h summation terms for F Number Tv = 0; Number fv = 0; for (unsigned int i = 0; i < dim; i++){ Tv += grad_T(i) * v(i); fv += grad_f(i) * v(i); } // Compute the time derivative of density const Number drho_dt = (rho - rho_old)/dt; // Compute alpha term of Eq. 69 const Number alpha = this->alpha(grad_T, grad_h, f); // Extract tau_1 stabilization term const Number tau_1 = thermo->tau_1(qp_vec[qp], elem_length); // Loop through the components and construct matrices for (unsigned int i = 0; i < N.size(); i++){ // Compute advective stabilization term (Eq. A, p. 1777) const Number d = tau_1 * v * B[i][qp] / f - tau_1 * 1/rho * drho_dt * (1-f)/f * N[i][qp]; // Force vector, Eq. 77 Number F1 = JxW[qp] * (N[i][qp] + d) * rho * (1 - f) * (cf - cs) * Tv; Number F2 = JxW[qp] * (N[i][qp] + d) * rho * fv * ((cf - cs) * (T - Te) + hf); Number F3 = JxW[qp] * (N[i][qp] + d) * drho_dt * (1 - f) * ((cf - cs) * (T - Te) + hf); Fe(i) += F1 + F2 + F3; // Build the stiffness matrices for (unsigned int j = 0; j < N.size(); j++){ // Mass matrix, Eq. 108 Me(i,j) += JxW[qp] * rho * ((N[i][qp] + d) * N[j][qp]); // Stiffness matrix one, Ne, Eq. 109 Ne(i,j) += JxW[qp] * rho * ((N[i][qp] + d) * (v * B[j][qp])); // Stiffness matrix two, Ke, Eq. 110 Ke(i,j) += JxW[qp]*((eps*kf + (1 - eps)*ks) * alpha * B[i][qp] * B[j][qp]); } } } printf("Me:\n"); Me.print(std::cout); printf("\nNe:\n"); Ne.print(std::cout); printf("\nKe:\n"); Ke.print(std::cout); printf("\nFe:\n"); Fe.print(std::cout); h.resize(dof_indices.size()); h_dot.resize(dof_indices.size()); // delta_h_dot.resize(dof_indices_deltahdot.size()); for (unsigned int i = 0; i < dof_indices.size(); i++){ h(i) = this->old_solution(dof_indices[i]); h_dot(i) = this->get_vector("h_dot")(dof_indices[i]); // delta_h_dot(i) = this->old_solution(dof_indices_deltahdot[i]); } this->get_matrix("M").add_matrix(Me, dof_indices); this->get_matrix("N").add_matrix(Ne, dof_indices); this->get_matrix("K").add_matrix(Ke, dof_indices); this->get_vector("F").add_vector(Fe, dof_indices); Mstar.resize(dof_indices.size(), dof_indices.size()); R.resize(dof_indices.size()); // Me + gamma*dt*(Ke + Ne); Mstar.add(1,Me); Mstar.add(gamma*dt,Ke); Mstar.add(gamma*dt,Ne); this->matrix->add_matrix(Mstar, dof_indices); R.add(1,Fe); DenseVector<Number> a(dof_indices.size()); Me.vector_mult(a, h_dot); R.add(-1, a); DenseMatrix<Number> B(dof_indices.size(), dof_indices.size()); DenseVector<Number> b(dof_indices.size()); B.add(1,Ne); B.add(1,Ke); B.vector_mult(b, h); R.add(-1,b); this->rhs->add_vector(R, dof_indices); /* // BOUNDARY CONDITIONS // Loop through each side of the element for applying boundary conditions for (unsigned int s = 0; s < elem->n_sides(); s++){ // Only consider the side if it does not have a neighbor if (elem->neighbor(s) == NULL){ // Pointer to current element side const AutoPtr<Elem> side = elem->side(s); // Boundary ID of the current side int boundary_id = (mesh.boundary_info)->boundary_id(elem, s); // Get index of the boundary class with the same id // this vector is empty if there is no match and only // contains a single value if there is a match std::vector<int> idx = get_boundary_index(boundary_id); // Continue of there is a match if(!idx.empty()){ // Compute the shape function values on the element face fe_face->reinit(elem, s); // Create a shared pointer to the boundary class boost::shared_ptr<HeatEqBoundaryBase> ptr = bc_ptrs[idx[0]]; // Determine the type of boundary considered std::string type = ptr->type; // Loop through quadrature points for (unsigned int qp = 0; qp < qface.n_points(); qp++){ // DIRICHLET (libMesh version; handled at initialization) if(type.compare("dirichlet") == 0){ // The dirichlet conditions are handled at initlization // but I don't want to throw an error if they are // encountered, so just do nothing // NEUMANN condition } else if(type.compare("neumann") == 0){ // Current and past flux values const Number q = ptr->q(qface_points[qp], time); const Number q_old = ptr->q(qface_points[qp], time - dt); // Add values to Fe for (unsigned int i = 0; i < psi.size(); i++){ Fe(i) += JxW_face[qp] * q * psi[i][qp]; Fe_old(i) += JxW_face[qp] * q_old * psi[i][qp]; } // CONVECTION boundary } else if(type.compare("convection") == 0){ // Current and past h and T_inf const Number h = ptr->h(qface_points[qp], time); const Number h_old = ptr->h(qface_points[qp], time - dt); const Number Tinf = ptr->Tinf(qface_points[qp], time); const Number Tinf_old = ptr->Tinf(qface_points[qp], time - dt); // Add values to Ke and Fe for (unsigned int i = 0; i < psi.size(); i++){ Fe(i) += (1) * JxW_face[qp] * h * Tinf * psi[i][qp]; Fe_old(i) += (1) * JxW_face[qp] * h_old * Tinf_old * psi[i][qp]; for (unsigned int j = 0; j < psi.size(); j++){ Ke(i,j) += JxW_face[qp] * psi[i][qp] * h * psi[j][qp]; } } // Un-registerd type } else { printf("WARNING! The boundary type, %s, was not understood!\n", type.c_str()); } // (end) type.compare(...) statemenst } //(end) for (int qp = 0; qp < qface.n_points(); qp++) } // (end) if(!idx.empty) } // (end) if (elem->neighbor(s) == NULL){ } // (end) for (int s = 0; s < elem->n_sides(); s++) // Zero the pervious time-step temperature vector for this element u_old.resize(dof_indices.size()); // Gather the temperatures at the nodes for (unsigned int i = 0; i < psi.size(); i++){ u_old(i) = this->old_solution(dof_indices[i]); } // Build K_hat and F_hat (appends existing) K_hat.resize(dof_indices.size(), dof_indices.size()); F_hat.resize(dof_indices.size()); build_stiffness_and_rhs(K_hat, F_hat, Me, Ke, Fe_old, Fe, u_old, dt, theta); // Applies the dirichlet constraints to K_hat and F_hat dof_map.heterogenously_constrain_element_matrix_and_vector(K_hat, F_hat, dof_indices); // Apply the local components to the global K and F this->matrix->add_matrix(K_hat, dof_indices); this->rhs->add_vector(F_hat, dof_indices); */ } // (end) for ( ; el != end_el; ++el) //update_rhs(); } // (end) assemble()
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; }
// 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 }
// 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_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); } }