void testRestart() { SlitFunc slitfunc; _mesh->write("slit_mesh.xda"); _es->write("slit_solution.xda", EquationSystems::WRITE_DATA | EquationSystems::WRITE_SERIAL_FILES); Mesh mesh2(*TestCommWorld); mesh2.read("slit_mesh.xda"); EquationSystems es2(mesh2); es2.read("slit_solution.xda"); System & sys2 = es2.get_system<System> ("SimpleSystem"); unsigned int dim = 2; CPPUNIT_ASSERT_EQUAL( sys2.n_vars(), 1u ); FEMContext context(sys2); FEBase * fe = NULL; context.get_element_fe( 0, fe, dim ); const std::vector<Point> & xyz = fe->get_xyz(); fe->get_phi(); MeshBase::const_element_iterator el = mesh2.active_local_elements_begin(); const MeshBase::const_element_iterator end_el = mesh2.active_local_elements_end(); for (; el != end_el; ++el) { const Elem * elem = *el; context.pre_fe_reinit(sys2, elem); context.elem_fe_reinit(); const unsigned int n_qp = xyz.size(); for (unsigned int qp=0; qp != n_qp; ++qp) { const Number exact_val = slitfunc(context, xyz[qp]); const Number discrete_val = context.interior_value(0, qp); CPPUNIT_ASSERT_DOUBLES_EQUAL(libmesh_real(exact_val), libmesh_real(discrete_val), TOLERANCE*TOLERANCE); } } }
void MaxQpsThread::operator() (const ConstElemRange & range) { ParallelUniqueId puid; _tid = puid.id; // For short circuiting reinit std::set<ElemType> seen_it; for (ConstElemRange::const_iterator elem_it = range.begin() ; elem_it != range.end(); ++elem_it) { const Elem * elem = *elem_it; // Only reinit if the element type has not previously been seen if (seen_it.insert(elem->type()).second) { FEType fe_type(FIRST, LAGRANGE); unsigned int dim = elem->dim(); unsigned int side = 0; // we assume that any element will have at least one side ;) // We cannot mess with the FE objects in Assembly, because we might need to request second derivatives // later on. If we used them, we'd call reinit on them, thus making the call to request second // derivatives harmful (i.e. leading to segfaults/asserts). Thus, we have to use a locally allocated object here. FEBase * fe = FEBase::build(dim, fe_type).release(); // figure out the number of qps for volume QBase * qrule = QBase::build(_qtype, dim, _order).release(); fe->attach_quadrature_rule(qrule); fe->reinit(elem); if (qrule->n_points() > _max) _max = qrule->n_points(); delete qrule; // figure out the number of qps for the face // NOTE: user might specify higher order rule for faces, thus possibly ending up with more qps than in the volume QBase * qrule_face = QBase::build(_qtype, dim - 1, _face_order).release(); fe->attach_quadrature_rule(qrule_face); fe->reinit(elem, side); if (qrule_face->n_points() > _max) _max = qrule_face->n_points(); delete qrule_face; delete fe; } } }
void testSystem() { SlitFunc slitfunc; unsigned int dim = 2; CPPUNIT_ASSERT_EQUAL( _sys->n_vars(), 1u ); FEMContext context(*_sys); FEBase * fe = NULL; context.get_element_fe( 0, fe, dim ); const std::vector<Point> & xyz = fe->get_xyz(); fe->get_phi(); 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; context.pre_fe_reinit(*_sys, elem); context.elem_fe_reinit(); const unsigned int n_qp = xyz.size(); for (unsigned int qp=0; qp != n_qp; ++qp) { const Number exact_val = slitfunc(context, xyz[qp]); const Number discrete_val = context.interior_value(0, qp); CPPUNIT_ASSERT_DOUBLES_EQUAL(libmesh_real(exact_val), libmesh_real(discrete_val), TOLERANCE*TOLERANCE); } } }
void HeatSystem::init_context(DiffContext & context) { FEMContext & c = libmesh_cast_ref<FEMContext &>(context); const std::set<unsigned char> & elem_dims = c.elem_dimensions(); for (std::set<unsigned char>::const_iterator dim_it = elem_dims.begin(); dim_it != elem_dims.end(); ++dim_it) { const unsigned char dim = *dim_it; FEBase * fe = libmesh_nullptr; c.get_element_fe(T_var, fe, dim); fe->get_JxW(); // For integration fe->get_dphi(); // For bilinear form fe->get_xyz(); // For forcing fe->get_phi(); // For forcing } FEMSystem::init_context(context); }
bool HeatSystem::element_time_derivative (bool request_jacobian, DiffContext & context) { FEMContext & c = libmesh_cast_ref<FEMContext &>(context); const unsigned int mesh_dim = c.get_system().get_mesh().mesh_dimension(); // First we get some references to cell-specific data that // will be used to assemble the linear system. const unsigned int dim = c.get_elem().dim(); FEBase * fe = libmesh_nullptr; c.get_element_fe(T_var, fe, dim); // Element Jacobian * quadrature weights for interior integration const std::vector<Real> & JxW = fe->get_JxW(); const std::vector<Point> & xyz = fe->get_xyz(); const std::vector<std::vector<Real> > & phi = fe->get_phi(); const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi(); // The number of local degrees of freedom in each variable const unsigned int n_T_dofs = c.get_dof_indices(T_var).size(); // The subvectors and submatrices we need to fill: DenseSubMatrix<Number> & K = c.get_elem_jacobian(T_var, T_var); DenseSubVector<Number> & F = c.get_elem_residual(T_var); // Now we will build the element Jacobian and residual. // Constructing the residual 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. unsigned int n_qpoints = c.get_element_qrule().n_points(); for (unsigned int qp=0; qp != n_qpoints; qp++) { // Compute the solution gradient at the Newton iterate Gradient grad_T = c.interior_gradient(T_var, qp); const Number k = _k[dim]; const Point & p = xyz[qp]; // solution + laplacian depend on problem dimension const Number u_exact = (mesh_dim == 2) ? std::sin(libMesh::pi*p(0)) * std::sin(libMesh::pi*p(1)) : std::sin(libMesh::pi*p(0)) * std::sin(libMesh::pi*p(1)) * std::sin(libMesh::pi*p(2)); // Only apply forcing to interior elements const Number forcing = (dim == mesh_dim) ? -k * u_exact * (dim * libMesh::pi * libMesh::pi) : 0; const Number JxWxNK = JxW[qp] * -k; for (unsigned int i=0; i != n_T_dofs; i++) F(i) += JxWxNK * (grad_T * dphi[i][qp] + forcing * phi[i][qp]); if (request_jacobian) { const Number JxWxNKxD = JxWxNK * context.get_elem_solution_derivative(); for (unsigned int i=0; i != n_T_dofs; i++) for (unsigned int j=0; j != n_T_dofs; ++j) K(i,j) += JxWxNKxD * (dphi[i][qp] * dphi[j][qp]); } } // end of the quadrature point qp-loop return request_jacobian; }
// This function assembles the system matrix and right-hand-side // for the discrete form of our wave equation. void assemble_wave(EquationSystems & es, const std::string & system_name) { // It is a good idea to make sure we are assembling // the proper system. libmesh_assert_equal_to (system_name, "Wave"); #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS // Get a constant reference to the mesh object. const MeshBase & mesh = es.get_mesh(); // Get a reference to the system we are solving. LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>("Wave"); // A reference to the \p DofMap object for this system. The \p DofMap // object handles the index translation from node and element numbers // to degree of freedom numbers. const DofMap & dof_map = system.get_dof_map(); // The dimension that we are running. const unsigned int dim = mesh.mesh_dimension(); // Copy the speed of sound to a local variable. const Real speed = es.parameters.get<Real>("speed"); // Get a constant reference to the Finite Element type // for the first (and only) variable in the system. const FEType & fe_type = dof_map.variable_type(0); // Build a Finite Element object of the specified type. Since the // \p FEBase::build() member dynamically creates memory we will // store the object as an \p UniquePtr<FEBase>. Check ex5 for details. UniquePtr<FEBase> fe (FEBase::build(dim, fe_type)); // Do the same for an infinite element. UniquePtr<FEBase> inf_fe (FEBase::build_InfFE(dim, fe_type)); // A 2nd order Gauss quadrature rule for numerical integration. QGauss qrule (dim, SECOND); // Tell the finite element object to use our quadrature rule. fe->attach_quadrature_rule (&qrule); // Due to its internal structure, the infinite element handles // quadrature rules differently. It takes the quadrature // rule which has been initialized for the FE object, but // creates suitable quadrature rules by @e itself. The user // need not worry about this. inf_fe->attach_quadrature_rule (&qrule); // Define data structures to contain the element matrix // and right-hand-side vector contribution. Following // basic finite element terminology we will denote these // "Ke", "Ce", "Me", and "Fe" for the stiffness, damping // and mass matrices, and the load vector. Note that in // Acoustics, these descriptors though do @e not match the // true physical meaning of the projectors. The final // overall system, however, resembles the conventional // notation again. DenseMatrix<Number> Ke; DenseMatrix<Number> Ce; DenseMatrix<Number> Me; DenseVector<Number> Fe; // This vector will hold the degree of freedom indices for // the element. These define where in the global system // the element degrees of freedom get mapped. std::vector<dof_id_type> dof_indices; // Now we will loop over all the elements in the mesh. // We will compute the element matrix and right-hand-side // contribution. MeshBase::const_element_iterator el = mesh.active_local_elements_begin(); const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end(); for ( ; el != end_el; ++el) { // Store a pointer to the element we are currently // working on. This allows for nicer syntax later. const Elem * elem = *el; // Get the degree of freedom indices for the // current element. These define where in the global // matrix and right-hand-side this element will // contribute to. dof_map.dof_indices (elem, dof_indices); // The mesh contains both finite and infinite elements. These // elements are handled through different classes, namely // \p FE and \p InfFE, respectively. However, since both // are derived from \p FEBase, they share the same interface, // and overall burden of coding is @e greatly reduced through // using a pointer, which is adjusted appropriately to the // current element type. FEBase * cfe = libmesh_nullptr; // This here is almost the only place where we need to // distinguish between finite and infinite elements. // For faster computation, however, different approaches // may be feasible. // // Up to now, we do not know what kind of element we // have. Aske the element of what type it is: if (elem->infinite()) { // We have an infinite element. Let \p cfe point // to our \p InfFE object. This is handled through // an UniquePtr. Through the \p UniquePtr::get() we "borrow" // the pointer, while the \p UniquePtr \p inf_fe is // still in charge of memory management. cfe = inf_fe.get(); } else { // This is a conventional finite element. Let \p fe handle it. cfe = fe.get(); // Boundary conditions. // Here we just zero the rhs-vector. For natural boundary // conditions check e.g. previous examples. { // Zero the RHS for this element. Fe.resize (dof_indices.size()); system.rhs->add_vector (Fe, dof_indices); } // end boundary condition section } // else if (elem->infinite()) // This is slightly different from the Poisson solver: // Since the finite element object may change, we have to // initialize the constant references to the data fields // each time again, when a new element is processed. // // The element Jacobian * quadrature weight at each integration point. const std::vector<Real> & JxW = cfe->get_JxW(); // The element shape functions evaluated at the quadrature points. const std::vector<std::vector<Real> > & phi = cfe->get_phi(); // The element shape function gradients evaluated at the quadrature // points. const std::vector<std::vector<RealGradient> > & dphi = cfe->get_dphi(); // The infinite elements need more data fields than conventional FE. // These are the gradients of the phase term \p dphase, an additional // radial weight for the test functions \p Sobolev_weight, and its // gradient. // // Note that these data fields are also initialized appropriately by // the \p FE method, so that the weak form (below) is valid for @e both // finite and infinite elements. const std::vector<RealGradient> & dphase = cfe->get_dphase(); const std::vector<Real> & weight = cfe->get_Sobolev_weight(); const std::vector<RealGradient> & dweight = cfe->get_Sobolev_dweight(); // Now this is all independent of whether we use an \p FE // or an \p InfFE. Nice, hm? ;-) // // Compute the element-specific data, as described // in previous examples. cfe->reinit (elem); // Zero the element matrices. Boundary conditions were already // processed in the \p FE-only section, see above. Ke.resize (dof_indices.size(), dof_indices.size()); Ce.resize (dof_indices.size(), dof_indices.size()); Me.resize (dof_indices.size(), dof_indices.size()); // The total number of quadrature points for infinite elements // @e has to be determined in a different way, compared to // conventional finite elements. This type of access is also // valid for finite elements, so this can safely be used // anytime, instead of asking the quadrature rule, as // seen in previous examples. unsigned int max_qp = cfe->n_quadrature_points(); // Loop over the quadrature points. for (unsigned int qp=0; qp<max_qp; qp++) { // Similar to the modified access to the number of quadrature // points, the number of shape functions may also be obtained // in a different manner. This offers the great advantage // of being valid for both finite and infinite elements. const unsigned int n_sf = cfe->n_shape_functions(); // Now we will build the element matrices. Since the infinite // elements are based on a Petrov-Galerkin scheme, the // resulting system matrices are non-symmetric. The additional // weight, described before, is part of the trial space. // // For the finite elements, though, these matrices are symmetric // just as we know them, since the additional fields \p dphase, // \p weight, and \p dweight are initialized appropriately. // // test functions: weight[qp]*phi[i][qp] // trial functions: phi[j][qp] // phase term: phase[qp] // // derivatives are similar, but note that these are of type // Point, not of type Real. for (unsigned int i=0; i<n_sf; i++) for (unsigned int j=0; j<n_sf; j++) { // (ndt*Ht + nHt*d) * nH Ke(i,j) += ( (dweight[qp] * phi[i][qp] // Point * Real = Point + // + dphi[i][qp] * weight[qp] // Point * Real = Point ) * dphi[j][qp] ) * JxW[qp]; // (d*Ht*nmut*nH - ndt*nmu*Ht*H - d*nHt*nmu*H) Ce(i,j) += ( (dphase[qp] * dphi[j][qp]) // (Point * Point) = Real * weight[qp] * phi[i][qp] // * Real * Real = Real - // - (dweight[qp] * dphase[qp]) // (Point * Point) = Real * phi[i][qp] * phi[j][qp] // * Real * Real = Real - // - (dphi[i][qp] * dphase[qp]) // (Point * Point) = Real * weight[qp] * phi[j][qp] // * Real * Real = Real ) * JxW[qp]; // (d*Ht*H * (1 - nmut*nmu)) Me(i,j) += ( (1. - (dphase[qp] * dphase[qp])) // (Real - (Point * Point)) = Real * phi[i][qp] * phi[j][qp] * weight[qp] // * Real * Real * Real = Real ) * JxW[qp]; } // end of the matrix summation loop } // end of quadrature point loop // The element matrices are now built for this element. // Collect them in Ke, and then add them to the global matrix. // The \p SparseMatrix::add_matrix() member does this for us. Ke.add(1./speed , Ce); Ke.add(1./(speed*speed), Me); // If this assembly program were to be used on an adaptive mesh, // we would have to apply any hanging node constraint equations dof_map.constrain_element_matrix(Ke, dof_indices); system.matrix->add_matrix (Ke, dof_indices); } // end of element loop // Note that we have not applied any boundary conditions so far. // Here we apply a unit load at the node located at (0,0,0). { // Iterate over local nodes MeshBase::const_node_iterator nd = mesh.local_nodes_begin(); const MeshBase::const_node_iterator nd_end = mesh.local_nodes_end(); for (; nd != nd_end; ++nd) { // Get a reference to the current node. const Node & node = **nd; // Check the location of the current node. if (std::abs(node(0)) < TOLERANCE && std::abs(node(1)) < TOLERANCE && std::abs(node(2)) < TOLERANCE) { // The global number of the respective degree of freedom. unsigned int dn = node.dof_number(0,0,0); system.rhs->add (dn, 1.); } } } #else // dummy assert libmesh_assert_not_equal_to (es.get_mesh().mesh_dimension(), 1); #endif //ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS }
bool SolidSystem::side_time_derivative(bool request_jacobian, DiffContext &context) { FEMContext &c = libmesh_cast_ref<FEMContext&>(context); // Apply displacement boundary conditions with penalty method // Get the current load step Real ratio = this->get_equation_systems().parameters.get<Real>("progress") + 0.001; // The BC are stored in the simulation parameters as array containing sequences of // four numbers: Id of the side for the displacements and three values describing the // displacement. E.g.: bc/displacement = '5 nan nan -1.0'. This will move all nodes of // side 5 about 1.0 units down the z-axis while leaving all other directions unrestricted // Get number of BCs to enforce unsigned int num_bc = args.vector_variable_size("bc/displacement"); if (num_bc % 4 != 0) { libMesh::err << "ERROR, Odd number of values in displacement boundary condition.\n" << std::endl; libmesh_error(); } num_bc /= 4; // Loop over all BCs for (unsigned int nbc = 0; nbc < num_bc; nbc++) { // Get IDs of the side for this BC short int positive_boundary_id = args("bc/displacement", 1, nbc * 4); // The current side may not be on the boundary to be restricted if (!this->get_mesh().boundary_info->has_boundary_id (c.elem,c.side,positive_boundary_id)) continue; // Read values from configuration file Point diff_value; for (unsigned int d = 0; d < c.dim; ++d) { diff_value(d) = args("bc/displacement", NAN, nbc * 4 + 1 + d); } // Scale according to current load step diff_value *= ratio; Real penalty_number = args("bc/displacement_penalty", 1e7); FEBase * fe = c.side_fe_var[var[0]]; const std::vector<std::vector<Real> > & phi = fe->get_phi(); const std::vector<Real>& JxW = fe->get_JxW(); const std::vector<Point>& coords = fe->get_xyz(); unsigned int n_x_dofs = c.dof_indices_var[this->var[0]].size(); // get mappings for dofs for auxiliary system for original mesh positions const System & auxsys = this->get_equation_systems().get_system( "auxiliary"); const DofMap & auxmap = auxsys.get_dof_map(); std::vector<dof_id_type> undefo_dofs[3]; for (unsigned int d = 0; d < c.dim; ++d) { auxmap.dof_indices(c.elem, undefo_dofs[d], undefo_var[d]); } for (unsigned int qp = 0; qp < c.side_qrule->n_points(); ++qp) { // calculate coordinates of qp on undeformed mesh Point orig_point; for (unsigned int i = 0; i < n_x_dofs; ++i) { for (unsigned int d = 0; d < c.dim; ++d) { Number orig_val = auxsys.current_solution(undefo_dofs[d][i]); #if LIBMESH_USE_COMPLEX_NUMBERS orig_point(d) += phi[i][qp] * orig_val.real(); #else orig_point(d) += phi[i][qp] * orig_val; #endif } } // Calculate displacement to be enforced. Point diff = coords[qp] - orig_point - diff_value; // Assemble for (unsigned int i = 0; i < n_x_dofs; ++i) { for (unsigned int d1 = 0; d1 < c.dim; ++d1) { if (libmesh_isnan(diff(d1))) continue; Real val = JxW[qp] * phi[i][qp] * diff(d1) * penalty_number; c.elem_subresiduals[var[d1]]->operator ()(i) += val; } if (request_jacobian) { for (unsigned int j = 0; j < n_x_dofs; ++j) { for (unsigned int d1 = 0; d1 < c.dim; ++d1) { if (libmesh_isnan(diff(d1))) continue; Real val = JxW[qp] * phi[i][qp] * phi[j][qp] * penalty_number; c.elem_subjacobians[var[d1]][var[d1]]->operator ()(i, j) += val; } } } } } } return request_jacobian; }
void Assembly::reinitFE(const Elem * elem) { unsigned int dim = elem->dim(); std::map<FEType, FEBase *>::iterator it = _fe[dim].begin(); std::map<FEType, FEBase *>::iterator end = _fe[dim].end(); ElementFEShapeData * efesd = NULL; // Whether or not we're going to do FE caching this time through bool do_caching = _should_use_fe_cache && _currently_fe_caching; if (do_caching) { efesd = _element_fe_shape_data_cache[elem->id()]; if (!efesd) { efesd = new ElementFEShapeData; _element_fe_shape_data_cache[elem->id()] = efesd; efesd->_invalidated = true; } } for (; it != end; ++it) { FEBase * fe = it->second; const FEType & fe_type = it->first; _current_fe[fe_type] = fe; FEShapeData * fesd = _fe_shape_data[fe_type]; FEShapeData * cached_fesd = NULL; if (do_caching) cached_fesd = efesd->_shape_data[fe_type]; if (!cached_fesd || efesd->_invalidated) { fe->reinit(elem); fesd->_phi.shallowCopy(const_cast<std::vector<std::vector<Real> > &>(fe->get_phi())); fesd->_grad_phi.shallowCopy(const_cast<std::vector<std::vector<RealGradient> > &>(fe->get_dphi())); if (_need_second_derivative[fe_type]) fesd->_second_phi.shallowCopy(const_cast<std::vector<std::vector<RealTensor> > &>(fe->get_d2phi())); if (do_caching) { if (!cached_fesd) { cached_fesd = new FEShapeData; efesd->_shape_data[fe_type] = cached_fesd; } *cached_fesd = *fesd; } } else // This means we have valid cached shape function values for this element / fe_type combo { fesd->_phi.shallowCopy(cached_fesd->_phi); fesd->_grad_phi.shallowCopy(cached_fesd->_grad_phi); if (_need_second_derivative[fe_type]) fesd->_second_phi.shallowCopy(cached_fesd->_second_phi); } } // During that last loop the helper objects will have been reinitialized as well // We need to dig out the q_points and JxW from it. if (!do_caching || efesd->_invalidated) { _current_q_points.shallowCopy(const_cast<std::vector<Point> &>((*_holder_fe_helper[dim])->get_xyz())); _current_JxW.shallowCopy(const_cast<std::vector<Real> &>((*_holder_fe_helper[dim])->get_JxW())); if (do_caching) { efesd->_q_points = _current_q_points; efesd->_JxW = _current_JxW; } } else // Use cached values { _current_q_points.shallowCopy(efesd->_q_points); _current_JxW.shallowCopy(efesd->_JxW); } if (do_caching) efesd->_invalidated = false; }
void testRestart() { SlitFunc slitfunc; _mesh->write("slit_mesh.xda"); _es->write("slit_solution.xda", EquationSystems::WRITE_DATA | EquationSystems::WRITE_SERIAL_FILES); Mesh mesh2(*TestCommWorld); mesh2.read("slit_mesh.xda"); EquationSystems es2(mesh2); es2.read("slit_solution.xda"); System & sys2 = es2.get_system<System> ("SimpleSystem"); unsigned int dim = 2; CPPUNIT_ASSERT_EQUAL( sys2.n_vars(), 1u ); FEMContext context(sys2); FEBase * fe = NULL; context.get_element_fe( 0, fe, dim ); const std::vector<Point> & xyz = fe->get_xyz(); fe->get_phi(); // While we're in the middle of a unique id based test case, let's // make sure our unique ids were all read in correctly too. UniquePtr<PointLocatorBase> locator = _mesh->sub_point_locator(); MeshBase::const_element_iterator el = mesh2.active_local_elements_begin(); const MeshBase::const_element_iterator end_el = mesh2.active_local_elements_end(); for (; el != end_el; ++el) { const Elem * elem = *el; const Elem * mesh1_elem = (*locator)(elem->centroid()); if (mesh1_elem) { CPPUNIT_ASSERT_EQUAL( elem->unique_id(), mesh1_elem->unique_id() ); for (unsigned int n=0; n != elem->n_nodes(); ++n) { const Node & node = elem->node_ref(n); const Node & mesh1_node = mesh1_elem->node_ref(n); CPPUNIT_ASSERT_EQUAL( node.unique_id(), mesh1_node.unique_id() ); } } context.pre_fe_reinit(sys2, elem); context.elem_fe_reinit(); const unsigned int n_qp = xyz.size(); for (unsigned int qp=0; qp != n_qp; ++qp) { const Number exact_val = slitfunc(context, xyz[qp]); const Number discrete_val = context.interior_value(0, qp); CPPUNIT_ASSERT_DOUBLES_EQUAL(libmesh_real(exact_val), libmesh_real(discrete_val), TOLERANCE*TOLERANCE); } } }