void
AEFVUpwindInternalSideFlux::calcJacobian(unsigned int /*iside*/,
                                         dof_id_type /*ielem*/,
                                         dof_id_type /*ineig*/,
                                         const std::vector<Real> & libmesh_dbg_var(uvec1),
                                         const std::vector<Real> & libmesh_dbg_var(uvec2),
                                         const RealVectorValue & dwave,
                                         DenseMatrix<Real> & jac1,
                                         DenseMatrix<Real> & jac2) const
{
  mooseAssert(uvec1.size() == 1, "Invalid size for uvec1. Must be single variable coupling.");
  mooseAssert(uvec2.size() == 1, "Invalid size for uvec1. Must be single variable coupling.");

  // assign the size of Jacobian matrix, e.g. = (1, 1) for the advection equation
  jac1.resize(1, 1);
  jac2.resize(1, 1);

  // assume a constant velocity on the left
  RealVectorValue uadv1(1.0, 1.0, 1.0);

  // assume a constant velocity on the right
  RealVectorValue uadv2(1.0, 1.0, 1.0);

  // normal velocity on the left and right
  Real vdon1 = uadv1 * dwave;
  Real vdon2 = uadv2 * dwave;

  // calculate the so-called a^plus and a^minus
  Real aplus = 0.5 * (vdon1 + std::abs(vdon1));
  Real amins = 0.5 * (vdon2 - std::abs(vdon2));

  // finally calculate the Jacobian matrix
  jac1(0, 0) = aplus;
  jac2(0, 0) = amins;
}
void DenseMatrix<T>::_svd_lapack (DenseVector<T>& sigma, DenseMatrix<T>& U, DenseMatrix<T>& VT)
{
  // The calling sequence for dgetrf is:
  // DGESVD( JOBU, JOBVT, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, LWORK, INFO )


  //  JOBU    (input) CHARACTER*1
  //          Specifies options for computing all or part of the matrix U:
  //          = 'A':  all M columns of U are returned in array U:
  //          = 'S':  the first min(m,n) columns of U (the left singular
  //                  vectors) are returned in the array U;
  //          = 'O':  the first min(m,n) columns of U (the left singular
  //                  vectors) are overwritten on the array A;
  //          = 'N':  no columns of U (no left singular vectors) are
  //                  computed.
  char JOBU = 'S';

  //  JOBVT   (input) CHARACTER*1
  //          Specifies options for computing all or part of the matrix
  //          V**T:
  //          = 'A':  all N rows of V**T are returned in the array VT;
  //          = 'S':  the first min(m,n) rows of V**T (the right singular
  //                  vectors) are returned in the array VT;
  //          = 'O':  the first min(m,n) rows of V**T (the right singular
  //                  vectors) are overwritten on the array A;
  //          = 'N':  no rows of V**T (no right singular vectors) are
  //                  computed.
  char JOBVT = 'S';

  std::vector<T> sigma_val;
  std::vector<T> U_val;
  std::vector<T> VT_val;

  _svd_helper(JOBU, JOBVT, sigma_val, U_val, VT_val);

  // Load the singular values into sigma, ignore U_val and VT_val
  sigma.resize(sigma_val.size());
  for(unsigned int i=0; i<sigma_val.size(); i++)
    sigma(i) = sigma_val[i];

  int M = this->n();
  int N = this->m();
  int min_MN = (M < N) ? M : N;
  U.resize(M,min_MN);
  for(unsigned int i=0; i<U.m(); i++)
    for(unsigned int j=0; j<U.n(); j++)
    {
      unsigned int index = i + j*U.n();  // Column major storage
      U(i,j) = U_val[index];
    }

  VT.resize(min_MN,N);
  for(unsigned int i=0; i<VT.m(); i++)
    for(unsigned int j=0; j<VT.n(); j++)
    {
      unsigned int index = i + j*U.n(); // Column major storage
      VT(i,j) = VT_val[index];
    }

}
Beispiel #3
0
void assemble_poisson(EquationSystems & es,
                      const std::string & system_name)
{
  libmesh_assert_equal_to (system_name, "Poisson");

  const MeshBase & mesh = es.get_mesh();
  const unsigned int dim = mesh.mesh_dimension();
  LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>("Poisson");

  const DofMap & dof_map = system.get_dof_map();

  FEType fe_type = dof_map.variable_type(0);
  UniquePtr<FEBase> fe (FEBase::build(dim, fe_type));
  QGauss qrule (dim, FIFTH);
  fe->attach_quadrature_rule (&qrule);
  UniquePtr<FEBase> fe_face (FEBase::build(dim, fe_type));
  QGauss qface(dim-1, FIFTH);
  fe_face->attach_quadrature_rule (&qface);

  const std::vector<Real> & JxW = fe->get_JxW();
  const std::vector<std::vector<Real> > & phi = fe->get_phi();
  const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi();

  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  std::vector<dof_id_type> dof_indices;

  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();

  for ( ; el != end_el; ++el)
    {
      const Elem * elem = *el;

      dof_map.dof_indices (elem, dof_indices);

      fe->reinit (elem);

      Ke.resize (dof_indices.size(),
                 dof_indices.size());

      Fe.resize (dof_indices.size());

      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        {
          for (unsigned int i=0; i<phi.size(); i++)
            {
              Fe(i) += JxW[qp]*phi[i][qp];
              for (unsigned int j=0; j<phi.size(); j++)
                Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]);
            }
        }

      dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices);

      system.matrix->add_matrix (Ke, dof_indices);
      system.rhs->add_vector    (Fe, dof_indices);
    }
}
void AssembleOptimization::assemble_A_and_F()
{
  A_matrix->zero();
  F_vector->zero();

  const MeshBase & mesh = _sys.get_mesh();

  const unsigned int dim = mesh.mesh_dimension();
  const unsigned int u_var = _sys.variable_number ("u");

  const DofMap & dof_map = _sys.get_dof_map();
  FEType fe_type = dof_map.variable_type(u_var);
  UniquePtr<FEBase> fe (FEBase::build(dim, fe_type));
  QGauss qrule (dim, fe_type.default_quadrature_order());
  fe->attach_quadrature_rule (&qrule);

  const std::vector<Real> & JxW = fe->get_JxW();
  const std::vector<std::vector<Real> > & phi = fe->get_phi();
  const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi();

  std::vector<dof_id_type> dof_indices;

  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();

  for ( ; el != end_el; ++el)
    {
      const Elem * elem = *el;

      dof_map.dof_indices (elem, dof_indices);

      const unsigned int n_dofs = dof_indices.size();

      fe->reinit (elem);

      Ke.resize (n_dofs, n_dofs);
      Fe.resize (n_dofs);

      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        {
          for (unsigned int dof_i=0; dof_i<n_dofs; dof_i++)
            {
              for (unsigned int dof_j=0; dof_j<n_dofs; dof_j++)
                {
                  Ke(dof_i, dof_j) += JxW[qp] * (dphi[dof_j][qp]* dphi[dof_i][qp]);
                }
              Fe(dof_i) += JxW[qp] * phi[dof_i][qp];
            }
        }

      A_matrix->add_matrix (Ke, dof_indices);
      F_vector->add_vector (Fe, dof_indices);
    }

  A_matrix->close();
  F_vector->close();
}
void transpose(const DenseMatrix &A, DenseMatrix &Atranspose)
{
    Atranspose.resize(A.n, A.m);
    for(int j=0; j<A.n; ++j) for(int i=0; i<A.m; ++i){
        Atranspose(j,i)=A(i,j);
    }
}
Beispiel #6
0
void DenseMatrix<T>::get_transpose (DenseMatrix<T>& dest) const
{
  dest.resize(this->n(), this->m());

  for (unsigned int i=0; i<dest.m(); i++)
    for (unsigned int j=0; j<dest.n(); j++)
      dest(i,j) = (*this)(j,i);
}
Beispiel #7
0
void DenseMatrix<T>::get_principal_submatrix (unsigned int sub_m,
                                              unsigned int sub_n,
                                              DenseMatrix<T>& dest) const
{
  libmesh_assert( (sub_m <= this->m()) && (sub_n <= this->n()) );

  dest.resize(sub_m, sub_n);
  for(unsigned int i=0; i<sub_m; i++)
    for(unsigned int j=0; j<sub_n; j++)
      dest(i,j) = (*this)(i,j);
}
Beispiel #8
0
void
dataLoad(std::istream & stream, DenseMatrix<Real> & v, void * /*context*/)
{
  unsigned int nr = 0, nc = 0;
  stream.read((char *) &nr, sizeof(nr));
  stream.read((char *) &nc, sizeof(nc));
  v.resize(nr,nc);
  for (unsigned int i = 0; i < v.m(); i++)
    for (unsigned int j = 0; j < v.n(); j++)
    {
      Real r = 0;
      stream.read((char *) &r, sizeof(r));
      v(i, j) = r;
    }
}
void NonlinearNeoHookeCurrentConfig::get_linearized_stiffness(DenseMatrix<Real> & stiffness, unsigned int & i, unsigned int & j) {
  stiffness.resize(3, 3);

  double G_IK = (sigma * dphi[i][current_qp]) * dphi[j][current_qp];
  stiffness(0, 0) += G_IK;
  stiffness(1, 1) += G_IK;
  stiffness(2, 2) += G_IK;

  B_L.resize(3, 6);
  this->build_b_0_mat(i, B_L);
  B_K.resize(3, 6);
  this->build_b_0_mat(j, B_K);

  B_L.right_multiply(C_mat);
  B_L.right_multiply_transpose(B_K);
  B_L *= 1/F.det();

  stiffness += B_L;
}
Beispiel #10
0
  void testSVD()
  {
    DenseMatrix<Number> U, VT;
    DenseVector<Real> sigma;
    DenseMatrix<Number> A;

    A.resize(3, 2);
    A(0,0) = 1.0; A(0,1) = 2.0;
    A(1,0) = 3.0; A(1,1) = 4.0;
    A(2,0) = 5.0; A(2,1) = 6.0;

    A.svd(sigma, U, VT);

    // Solution for this case is (verified with numpy)
    DenseMatrix<Number> true_U(3,2), true_VT(2,2);
    DenseVector<Real> true_sigma(2);
    true_U(0,0) = -2.298476964000715e-01; true_U(0,1) = 8.834610176985250e-01;
    true_U(1,0) = -5.247448187602936e-01; true_U(1,1) = 2.407824921325463e-01;
    true_U(2,0) = -8.196419411205157e-01; true_U(2,1) = -4.018960334334318e-01;

    true_VT(0,0) = -6.196294838293402e-01; true_VT(0,1) = -7.848944532670524e-01;
    true_VT(1,0) = -7.848944532670524e-01; true_VT(1,1) = 6.196294838293400e-01;

    true_sigma(0) = 9.525518091565109e+00;
    true_sigma(1) = 5.143005806586446e-01;

    for (unsigned i=0; i<U.m(); ++i)
      for (unsigned j=0; j<U.n(); ++j)
        CPPUNIT_ASSERT_DOUBLES_EQUAL( libmesh_real(U(i,j)), libmesh_real(true_U(i,j)), TOLERANCE*TOLERANCE);

    for (unsigned i=0; i<VT.m(); ++i)
      for (unsigned j=0; j<VT.n(); ++j)
        CPPUNIT_ASSERT_DOUBLES_EQUAL( libmesh_real(VT(i,j)), libmesh_real(true_VT(i,j)), TOLERANCE*TOLERANCE);

    for (unsigned i=0; i<sigma.size(); ++i)
      CPPUNIT_ASSERT_DOUBLES_EQUAL(sigma(i), true_sigma(i), TOLERANCE*TOLERANCE);
  }
Beispiel #11
0
// 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
}
Beispiel #12
0
// 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!
}
Beispiel #13
0
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
}
Beispiel #14
0
void assemble_matrices(EquationSystems& es,
                       const std::string& system_name)
{
  
  // It is a good idea to make sure we are assembling
  // the proper system.
  libmesh_assert_equal_to (system_name, "Eigensystem");

#ifdef LIBMESH_HAVE_SLEPC

  // Get a constant reference to the mesh object.
  const MeshBase& mesh = es.get_mesh();

  // The dimension that we are running.
  const unsigned int dim = mesh.mesh_dimension();

  // Get a reference to our system.
  EigenSystem & eigen_system = es.get_system<EigenSystem> (system_name);

  // Get a constant reference to the Finite Element type
  // for the first (and only) variable in the system.
  FEType fe_type = eigen_system.get_dof_map().variable_type(0);

  // A reference to the two system matrices
  SparseMatrix<Number>&  matrix_A = *eigen_system.matrix_A;
  SparseMatrix<Number>&  matrix_B = *eigen_system.matrix_B;

  // Build a Finite Element object of the specified type.  Since the
  // \p FEBase::build() member dynamically creates memory we will
  // store the object as an \p AutoPtr<FEBase>.  This can be thought
  // of as a pointer that will clean up after itself.
  AutoPtr<FEBase> fe (FEBase::build(dim, fe_type));
  
  // A  Gauss quadrature rule for numerical integration.
  // Use the default quadrature order.
  QGauss qrule (dim, fe_type.default_quadrature_order());

  // Tell the finite element object to use our quadrature rule.
  fe->attach_quadrature_rule (&qrule);

  // The element Jacobian * quadrature weight at each integration point.   
  const std::vector<Real>& JxW = fe->get_JxW();

  // The element shape functions evaluated at the quadrature points.
  const std::vector<std::vector<Real> >& phi = fe->get_phi();

  // The element shape function gradients evaluated at the quadrature
  // points.
  const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi();

  // A reference to the \p DofMap object for this system.  The \p DofMap
  // object handles the index translation from node and element numbers
  // to degree of freedom numbers.
  const DofMap& dof_map = eigen_system.get_dof_map();

  // The element mass and stiffness matrices.
  DenseMatrix<Number>   Me;
  DenseMatrix<Number>   Ke;

  // This vector will hold the degree of freedom indices for
  // the element.  These define where in the global system
  // the element degrees of freedom get mapped.
  std::vector<unsigned int> dof_indices;


  // Now we will loop over all the elements in the mesh that
  // live on the local processor. We will compute the element
  // matrix and right-hand-side contribution.  In case users
  // later modify this program to include refinement, we will
  // be safe and will only consider the active elements;
  // hence we use a variant of the \p active_elem_iterator.
  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();
 
  for ( ; el != end_el; ++el)
    {
      // Store a pointer to the element we are currently
      // working on.  This allows for nicer syntax later.
      const Elem* elem = *el;

      // Get the degree of freedom indices for the
      // current element.  These define where in the global
      // matrix and right-hand-side this element will
      // contribute to.
      dof_map.dof_indices (elem, dof_indices);

      // Compute the element-specific data for the current
      // element.  This involves computing the location of the
      // quadrature points (q_point) and the shape functions
      // (phi, dphi) for the current element.
      fe->reinit (elem);

      // Zero the element matrices before
      // summing them.  We use the resize member here because
      // the number of degrees of freedom might have changed from
      // the last element.  Note that this will be the case if the
      // element type is different (i.e. the last element was a
      // triangle, now we are on a quadrilateral).
      Ke.resize (dof_indices.size(), dof_indices.size());
      Me.resize (dof_indices.size(), dof_indices.size());

      // Now loop over the quadrature points.  This handles
      // the numeric integration.
      // 
      // We will build the element matrix.  This involves
      // a double loop to integrate the test funcions (i) against
      // the trial functions (j).
      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        for (unsigned int i=0; i<phi.size(); i++)
          for (unsigned int j=0; j<phi.size(); j++)
            {
              Me(i,j) += JxW[qp]*phi[i][qp]*phi[j][qp];
              Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]);
            }

      // On an unrefined mesh, constrain_element_matrix does
      // nothing.  If this assembly function is ever repurposed to
      // run on a refined mesh, getting the hanging node constraints
      // right will be important.  Note that, even with
      // asymmetric_constraint_rows = false, the constrained dof
      // diagonals still exist in the matrix, with diagonal entries
      // that are there to ensure non-singular matrices for linear
      // solves but which would generate positive non-physical
      // eigenvalues for eigensolves.
      // dof_map.constrain_element_matrix(Ke, dof_indices, false);
      // dof_map.constrain_element_matrix(Me, dof_indices, false);

      // Finally, simply add the element contribution to the
      // overall matrices A and B.
      matrix_A.add_matrix (Ke, dof_indices);
      matrix_B.add_matrix (Me, dof_indices);

    } // end of element loop


#endif // LIBMESH_HAVE_SLEPC

  /**
   * All done!
   */
  return;

}
void assemble_stokes (EquationSystems& es,
                      const std::string& system_name)
{
  // It is a good idea to make sure we are assembling
  // the proper system.
  libmesh_assert_equal_to (system_name, "Stokes");

  // Get a constant reference to the mesh object.
  const MeshBase& mesh = es.get_mesh();

  // The dimension that we are running
  const unsigned int dim = mesh.mesh_dimension();

  // Get a reference to the Convection-Diffusion system object.
  LinearImplicitSystem & system =
    es.get_system<LinearImplicitSystem> ("Stokes");

  // Numeric ids corresponding to each variable in the system
  const unsigned int u_var = system.variable_number ("u");
  const unsigned int v_var = system.variable_number ("v");
  const unsigned int p_var = system.variable_number ("p");

  // Get the Finite Element type for "u".  Note this will be
  // the same as the type for "v".
  FEType fe_vel_type = system.variable_type(u_var);

  // Get the Finite Element type for "p".
  FEType fe_pres_type = system.variable_type(p_var);

  // Build a Finite Element object of the specified type for
  // the velocity variables.
  UniquePtr<FEBase> fe_vel  (FEBase::build(dim, fe_vel_type));

  // Build a Finite Element object of the specified type for
  // the pressure variables.
  UniquePtr<FEBase> fe_pres (FEBase::build(dim, fe_pres_type));

  // A Gauss quadrature rule for numerical integration.
  // Let the \p FEType object decide what order rule is appropriate.
  QGauss qrule (dim, fe_vel_type.default_quadrature_order());

  // Tell the finite element objects to use our quadrature rule.
  fe_vel->attach_quadrature_rule (&qrule);
  fe_pres->attach_quadrature_rule (&qrule);

  // Here we define some references to cell-specific data that
  // will be used to assemble the linear system.
  //
  // The element Jacobian * quadrature weight at each integration point.
  const std::vector<Real>& JxW = fe_vel->get_JxW();

  // The element shape function gradients for the velocity
  // variables evaluated at the quadrature points.
  const std::vector<std::vector<RealGradient> >& dphi = fe_vel->get_dphi();

  // The element shape functions for the pressure variable
  // evaluated at the quadrature points.
  const std::vector<std::vector<Real> >& psi = fe_pres->get_phi();

  // A reference to the \p DofMap object for this system.  The \p DofMap
  // object handles the index translation from node and element numbers
  // to degree of freedom numbers.  We will talk more about the \p DofMap
  // in future examples.
  const DofMap & dof_map = system.get_dof_map();

  // Define data structures to contain the element matrix
  // and right-hand-side vector contribution.  Following
  // basic finite element terminology we will denote these
  // "Ke" and "Fe".
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  DenseSubMatrix<Number>
    Kuu(Ke), Kuv(Ke), Kup(Ke),
    Kvu(Ke), Kvv(Ke), Kvp(Ke),
    Kpu(Ke), Kpv(Ke), Kpp(Ke);

  DenseSubVector<Number>
    Fu(Fe),
    Fv(Fe),
    Fp(Fe);

  // This vector will hold the degree of freedom indices for
  // the element.  These define where in the global system
  // the element degrees of freedom get mapped.
  std::vector<dof_id_type> dof_indices;
  std::vector<dof_id_type> dof_indices_u;
  std::vector<dof_id_type> dof_indices_v;
  std::vector<dof_id_type> dof_indices_p;

  // Now we will loop over all the elements in the mesh that
  // live on the local processor. We will compute the element
  // matrix and right-hand-side contribution.  In case users later
  // modify this program to include refinement, we will be safe and
  // will only consider the active elements; hence we use a variant of
  // the \p active_elem_iterator.

  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();

  for ( ; el != end_el; ++el)
    {
      // Store a pointer to the element we are currently
      // working on.  This allows for nicer syntax later.
      const Elem* elem = *el;

      // Get the degree of freedom indices for the
      // current element.  These define where in the global
      // matrix and right-hand-side this element will
      // contribute to.
      dof_map.dof_indices (elem, dof_indices);
      dof_map.dof_indices (elem, dof_indices_u, u_var);
      dof_map.dof_indices (elem, dof_indices_v, v_var);
      dof_map.dof_indices (elem, dof_indices_p, p_var);

      const unsigned int n_dofs   = dof_indices.size();
      const unsigned int n_u_dofs = dof_indices_u.size();
      const unsigned int n_v_dofs = dof_indices_v.size();
      const unsigned int n_p_dofs = dof_indices_p.size();

      // Compute the element-specific data for the current
      // element.  This involves computing the location of the
      // quadrature points (q_point) and the shape functions
      // (phi, dphi) for the current element.
      fe_vel->reinit  (elem);
      fe_pres->reinit (elem);

      // Zero the element matrix and right-hand side before
      // summing them.  We use the resize member here because
      // the number of degrees of freedom might have changed from
      // the last element.  Note that this will be the case if the
      // element type is different (i.e. the last element was a
      // triangle, now we are on a quadrilateral).
      Ke.resize (n_dofs, n_dofs);
      Fe.resize (n_dofs);

      // Reposition the submatrices...  The idea is this:
      //
      //         -           -          -  -
      //        | Kuu Kuv Kup |        | Fu |
      //   Ke = | Kvu Kvv Kvp |;  Fe = | Fv |
      //        | Kpu Kpv Kpp |        | Fp |
      //         -           -          -  -
      //
      // The \p DenseSubMatrix.repostition () member takes the
      // (row_offset, column_offset, row_size, column_size).
      //
      // Similarly, the \p DenseSubVector.reposition () member
      // takes the (row_offset, row_size)
      Kuu.reposition (u_var*n_u_dofs, u_var*n_u_dofs, n_u_dofs, n_u_dofs);
      Kuv.reposition (u_var*n_u_dofs, v_var*n_u_dofs, n_u_dofs, n_v_dofs);
      Kup.reposition (u_var*n_u_dofs, p_var*n_u_dofs, n_u_dofs, n_p_dofs);

      Kvu.reposition (v_var*n_v_dofs, u_var*n_v_dofs, n_v_dofs, n_u_dofs);
      Kvv.reposition (v_var*n_v_dofs, v_var*n_v_dofs, n_v_dofs, n_v_dofs);
      Kvp.reposition (v_var*n_v_dofs, p_var*n_v_dofs, n_v_dofs, n_p_dofs);

      Kpu.reposition (p_var*n_u_dofs, u_var*n_u_dofs, n_p_dofs, n_u_dofs);
      Kpv.reposition (p_var*n_u_dofs, v_var*n_u_dofs, n_p_dofs, n_v_dofs);
      Kpp.reposition (p_var*n_u_dofs, p_var*n_u_dofs, n_p_dofs, n_p_dofs);

      Fu.reposition (u_var*n_u_dofs, n_u_dofs);
      Fv.reposition (v_var*n_u_dofs, n_v_dofs);
      Fp.reposition (p_var*n_u_dofs, n_p_dofs);

      // Now we will build the element matrix.
      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        {
          // Assemble the u-velocity row
          // uu coupling
          for (unsigned int i=0; i<n_u_dofs; i++)
            for (unsigned int j=0; j<n_u_dofs; j++)
              Kuu(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]);

          // up coupling
          for (unsigned int i=0; i<n_u_dofs; i++)
            for (unsigned int j=0; j<n_p_dofs; j++)
              Kup(i,j) += -JxW[qp]*psi[j][qp]*dphi[i][qp](0);


          // Assemble the v-velocity row
          // vv coupling
          for (unsigned int i=0; i<n_v_dofs; i++)
            for (unsigned int j=0; j<n_v_dofs; j++)
              Kvv(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]);

          // vp coupling
          for (unsigned int i=0; i<n_v_dofs; i++)
            for (unsigned int j=0; j<n_p_dofs; j++)
              Kvp(i,j) += -JxW[qp]*psi[j][qp]*dphi[i][qp](1);


          // Assemble the pressure row
          // pu coupling
          for (unsigned int i=0; i<n_p_dofs; i++)
            for (unsigned int j=0; j<n_u_dofs; j++)
              Kpu(i,j) += -JxW[qp]*psi[i][qp]*dphi[j][qp](0);

          // pv coupling
          for (unsigned int i=0; i<n_p_dofs; i++)
            for (unsigned int j=0; j<n_v_dofs; j++)
              Kpv(i,j) += -JxW[qp]*psi[i][qp]*dphi[j][qp](1);

        } // end of the quadrature point qp-loop

      // At this point the interior element integration has
      // been completed.  However, we have not yet addressed
      // boundary conditions.  For this example we will only
      // consider simple Dirichlet boundary conditions imposed
      // via the penalty method. The penalty method used here
      // is equivalent (for Lagrange basis functions) to lumping
      // the matrix resulting from the L2 projection penalty
      // approach introduced in example 3.
      {
        // The following loops over the sides of the element.
        // If the element has no neighbor on a side then that
        // side MUST live on a boundary of the domain.
        for (unsigned int s=0; s<elem->n_sides(); s++)
          if (elem->neighbor(s) == NULL)
            {
              UniquePtr<Elem> side (elem->build_side(s));

              // Loop over the nodes on the side.
              for (unsigned int ns=0; ns<side->n_nodes(); ns++)
                {
                  // The location on the boundary of the current
                  // node.

                  // const Real xf = side->point(ns)(0);
                  const Real yf = side->point(ns)(1);

                  // The penalty value.  \f$ \frac{1}{\epsilon \f$
                  const Real penalty = 1.e10;

                  // The boundary values.

                  // Set u = 1 on the top boundary, 0 everywhere else
                  const Real u_value = (yf > .99) ? 1. : 0.;

                  // Set v = 0 everywhere
                  const Real v_value = 0.;

                  // Find the node on the element matching this node on
                  // the side.  That defined where in the element matrix
                  // the boundary condition will be applied.
                  for (unsigned int n=0; n<elem->n_nodes(); n++)
                    if (elem->node(n) == side->node(ns))
                      {
                        // Matrix contribution.
                        Kuu(n,n) += penalty;
                        Kvv(n,n) += penalty;

                        // Right-hand-side contribution.
                        Fu(n) += penalty*u_value;
                        Fv(n) += penalty*v_value;
                      }
                } // end face node loop
            } // end if (elem->neighbor(side) == NULL)
      } // end boundary condition section

      // If this assembly program were to be used on an adaptive mesh,
      // we would have to apply any hanging node constraint equations.
      dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices);

      // The element matrix and right-hand-side are now built
      // for this element.  Add them to the global matrix and
      // right-hand-side vector.  The \p NumericMatrix::add_matrix()
      // and \p NumericVector::add_vector() members do this for us.
      system.matrix->add_matrix (Ke, dof_indices);
      system.rhs->add_vector    (Fe, dof_indices);
    } // end of element loop

  // That's it.
  return;
}
void
MultiAppProjectionTransfer::assembleL2To(EquationSystems & es, const std::string & system_name)
{
  unsigned int app = es.parameters.get<unsigned int>("app");

  FEProblem & from_problem = *_multi_app->problem();
  EquationSystems & from_es = from_problem.es();

  MooseVariable & from_var = from_problem.getVariable(0, _from_var_name);
  System & from_sys = from_var.sys().system();
  unsigned int from_var_num = from_sys.variable_number(from_var.name());

  NumericVector<Number> * serialized_from_solution = NumericVector<Number>::build(from_sys.comm()).release();
  serialized_from_solution->init(from_sys.n_dofs(), false, SERIAL);
  // Need to pull down a full copy of this vector on every processor so we can get values in parallel
  from_sys.solution->localize(*serialized_from_solution);

  MeshFunction from_func(from_es, *serialized_from_solution, from_sys.get_dof_map(), from_var_num);
  from_func.init(Trees::ELEMENTS);
  from_func.enable_out_of_mesh_mode(0.);


  const MeshBase& mesh = es.get_mesh();
  const unsigned int dim = mesh.mesh_dimension();

  LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>(system_name);

  FEType fe_type = system.variable_type(0);
  AutoPtr<FEBase> fe(FEBase::build(dim, fe_type));
  QGauss qrule(dim, fe_type.default_quadrature_order());
  fe->attach_quadrature_rule(&qrule);
  const std::vector<Real> & JxW = fe->get_JxW();
  const std::vector<std::vector<Real> > & phi = fe->get_phi();
  const std::vector<Point> & xyz = fe->get_xyz();

  const DofMap& dof_map = system.get_dof_map();
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;
  std::vector<dof_id_type> dof_indices;

  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();
  for ( ; el != end_el; ++el)
  {
    const Elem* elem = *el;

    fe->reinit (elem);

    dof_map.dof_indices (elem, dof_indices);
    Ke.resize (dof_indices.size(), dof_indices.size());
    Fe.resize (dof_indices.size());

    for (unsigned int qp = 0; qp < qrule.n_points(); qp++)
    {
      Point qpt = xyz[qp];
      Point pt = qpt + _multi_app->position(app);
      Real f = from_func(pt);

      // Now compute the element matrix and RHS contributions.
      for (unsigned int i=0; i<phi.size(); i++)
      {
        // RHS
        Fe(i) += JxW[qp] * (f * phi[i][qp]);

        if (_compute_matrix)
          for (unsigned int j = 0; j < phi.size(); j++)
          {
            // The matrix contribution
            Ke(i,j) += JxW[qp] * (phi[i][qp] * phi[j][qp]);
          }
      }
      dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices);

      if (_compute_matrix)
        system.matrix->add_matrix(Ke, dof_indices);
      system.rhs->add_vector(Fe, dof_indices);
    }
  }
}
void
MultiAppProjectionTransfer::assembleL2(EquationSystems & es, const std::string & system_name)
{
  // Get the system and mesh from the input arguments.
  LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>(system_name);
  MeshBase & to_mesh = es.get_mesh();

  // Get the meshfunction evaluations and the map that was stashed in the es.
  std::vector<Real> & final_evals = * es.parameters.get<std::vector<Real>*>("final_evals");
  std::map<unsigned int, unsigned int> & element_map = * es.parameters.get<std::map<unsigned int, unsigned int>*>("element_map");

  // Setup system vectors and matrices.
  FEType fe_type = system.variable_type(0);
  std::unique_ptr<FEBase> fe(FEBase::build(to_mesh.mesh_dimension(), fe_type));
  QGauss qrule(to_mesh.mesh_dimension(), fe_type.default_quadrature_order());
  fe->attach_quadrature_rule(&qrule);
  const DofMap& dof_map = system.get_dof_map();
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;
  std::vector<dof_id_type> dof_indices;
  const std::vector<Real> & JxW = fe->get_JxW();
  const std::vector<std::vector<Real> > & phi = fe->get_phi();

  const MeshBase::const_element_iterator end_el = to_mesh.active_local_elements_end();
  for (MeshBase::const_element_iterator el = to_mesh.active_local_elements_begin(); el != end_el; ++el)
  {
    const Elem* elem = *el;
    fe->reinit (elem);

    dof_map.dof_indices (elem, dof_indices);
    Ke.resize (dof_indices.size(), dof_indices.size());
    Fe.resize (dof_indices.size());

    for (unsigned int qp = 0; qp < qrule.n_points(); qp++)
    {
      Real meshfun_eval = 0.;
      if (element_map.find(elem->id()) != element_map.end())
      {
        // We have evaluations for this element.
        meshfun_eval = final_evals[element_map[elem->id()] + qp];
      }

      // Now compute the element matrix and RHS contributions.
      for (unsigned int i=0; i<phi.size(); i++)
      {
        // RHS
        Fe(i) += JxW[qp] * (meshfun_eval * phi[i][qp]);

        if (_compute_matrix)
          for (unsigned int j = 0; j < phi.size(); j++)
          {
            // The matrix contribution
            Ke(i,j) += JxW[qp] * (phi[i][qp] * phi[j][qp]);
          }
      }
      dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices);

      if (_compute_matrix)
        system.matrix->add_matrix(Ke, dof_indices);
      system.rhs->add_vector(Fe, dof_indices);
    }
  }
}
Beispiel #18
0
// We now define the matrix assembly function for the
// Laplace system.  We need to first compute element volume
// matrices, and then take into account the boundary
// conditions and the flux integrals, which will be handled
// via an interior penalty method.
void assemble_ellipticdg(EquationSystems& es, const std::string& system_name)
{
  std::cout<<" assembling elliptic dg system... ";
  std::cout.flush();

  // It is a good idea to make sure we are assembling
  // the proper system.
  libmesh_assert_equal_to (system_name, "EllipticDG");

  // Get a constant reference to the mesh object.
  const MeshBase& mesh = es.get_mesh();
  // The dimension that we are running
  const unsigned int dim = mesh.mesh_dimension();

  // Get a reference to the LinearImplicitSystem we are solving
  LinearImplicitSystem & ellipticdg_system = es.get_system<LinearImplicitSystem> ("EllipticDG");
  // Get some parameters that we need during assembly
  const Real penalty = es.parameters.get<Real> ("penalty");
  std::string refinement_type = es.parameters.get<std::string> ("refinement");

  // A reference to the \p DofMap object for this system.  The \p DofMap
  // object handles the index translation from node and element numbers
  // to degree of freedom numbers.  We will talk more about the \p DofMap
  const DofMap & dof_map = ellipticdg_system.get_dof_map();

  // Get a constant reference to the Finite Element type
  // for the first (and only) variable in the system.
  FEType fe_type = ellipticdg_system.variable_type(0);

  // Build a Finite Element object of the specified type.  Since the
  // \p FEBase::build() member dynamically creates memory we will
  // store the object as an \p AutoPtr<FEBase>.  This can be thought
  // of as a pointer that will clean up after itself.
  AutoPtr<FEBase> fe  (FEBase::build(dim, fe_type));
  AutoPtr<FEBase> fe_elem_face(FEBase::build(dim, fe_type));
  AutoPtr<FEBase> fe_neighbor_face(FEBase::build(dim, fe_type));

  // Quadrature rules for numerical integration.
#ifdef QORDER
  QGauss qrule (dim, QORDER);
#else
  QGauss qrule (dim, fe_type.default_quadrature_order());
#endif
  fe->attach_quadrature_rule (&qrule);

#ifdef QORDER
  QGauss qface(dim-1, QORDER);
#else
  QGauss qface(dim-1, fe_type.default_quadrature_order());
#endif

  // Tell the finite element object to use our quadrature rule.
  fe_elem_face->attach_quadrature_rule(&qface);
  fe_neighbor_face->attach_quadrature_rule(&qface);

  // Here we define some references to cell-specific data that
  // will be used to assemble the linear system.
  // Data for interior volume integrals
  const std::vector<Real>& JxW = fe->get_JxW();
  const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi();

  // Data for surface integrals on the element boundary
  const std::vector<std::vector<Real> >&  phi_face = fe_elem_face->get_phi();
  const std::vector<std::vector<RealGradient> >& dphi_face = fe_elem_face->get_dphi();
  const std::vector<Real>& JxW_face = fe_elem_face->get_JxW();
  const std::vector<Point>& qface_normals = fe_elem_face->get_normals();
  const std::vector<Point>& qface_points = fe_elem_face->get_xyz();

  // Data for surface integrals on the neighbor boundary
  const std::vector<std::vector<Real> >&  phi_neighbor_face = fe_neighbor_face->get_phi();
  const std::vector<std::vector<RealGradient> >& dphi_neighbor_face = fe_neighbor_face->get_dphi();

  // Define data structures to contain the element interior matrix
  // and right-hand-side vector contribution.  Following
  // basic finite element terminology we will denote these
  // "Ke" and "Fe".
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  // Data structures to contain the element and neighbor boundary matrix
  // contribution. This matrices will do the coupling beetwen the dofs of
  // the element and those of his neighbors.
  // Ken: matrix coupling elem and neighbor dofs
  DenseMatrix<Number> Kne;
  DenseMatrix<Number> Ken;
  DenseMatrix<Number> Kee;
  DenseMatrix<Number> Knn;

  // This vector will hold the degree of freedom indices for
  // the element.  These define where in the global system
  // the element degrees of freedom get mapped.
  std::vector<dof_id_type> dof_indices;

  // Now we will loop over all the elements in the mesh.  We will
  // compute first the element interior matrix and right-hand-side contribution
  // and then the element and neighbors boundary matrix contributions.
  MeshBase::const_element_iterator el = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();

  for ( ; el != end_el; ++el)
    {
      // Store a pointer to the element we are currently
      // working on.  This allows for nicer syntax later.
      const Elem* elem = *el;

      // Get the degree of freedom indices for the
      // current element.  These define where in the global
      // matrix and right-hand-side this element will
      // contribute to.
      dof_map.dof_indices (elem, dof_indices);
      const unsigned int n_dofs   = dof_indices.size();

      // Compute the element-specific data for the current
      // element.  This involves computing the location of the
      // quadrature points (q_point) and the shape functions
      // (phi, dphi) for the current element.
      fe->reinit  (elem);

      // Zero the element matrix and right-hand side before
      // summing them.  We use the resize member here because
      // the number of degrees of freedom might have changed from
      // the last element.
      Ke.resize (n_dofs, n_dofs);
      Fe.resize (n_dofs);

      // Now we will build the element interior matrix.  This involves
      // a double loop to integrate the test funcions (i) against
      // the trial functions (j).
      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        {
          for (unsigned int i=0; i<n_dofs; i++)
            {
              for (unsigned int j=0; j<n_dofs; j++)
                {
                  Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]);
                }
            }
        }

      // Now we adress boundary conditions.
      // We consider Dirichlet bc imposed via the interior penalty method
      // The following loops over the sides of the element.
      // If the element has no neighbor on a side then that
      // side MUST live on a boundary of the domain.
      for (unsigned int side=0; side<elem->n_sides(); side++)
        {
          if (elem->neighbor(side) == NULL)
            {
              // Pointer to the element face
              fe_elem_face->reinit(elem, side);

              AutoPtr<Elem> elem_side (elem->build_side(side));
              // h elemet dimension to compute the interior penalty penalty parameter
              const unsigned int elem_b_order = static_cast<unsigned int> (fe_elem_face->get_order());
              const double h_elem = elem->volume()/elem_side->volume() * 1./pow(elem_b_order, 2.);

              for (unsigned int qp=0; qp<qface.n_points(); qp++)
                {
                  Number bc_value = exact_solution(qface_points[qp], es.parameters,"null","void");
                  for (unsigned int i=0; i<n_dofs; i++)
                    {
                      // Matrix contribution
                      for (unsigned int j=0; j<n_dofs; j++)
                        {
                          Ke(i,j) += JxW_face[qp] * penalty/h_elem * phi_face[i][qp] * phi_face[j][qp]; // stability
                          Ke(i,j) -= JxW_face[qp] * (phi_face[i][qp] * (dphi_face[j][qp]*qface_normals[qp]) + phi_face[j][qp] * (dphi_face[i][qp]*qface_normals[qp])); // consistency
                        }
                      // RHS contribution
                      Fe(i) += JxW_face[qp] * bc_value * penalty/h_elem * phi_face[i][qp];      // stability
                      Fe(i) -= JxW_face[qp] * dphi_face[i][qp] * (bc_value*qface_normals[qp]);     // consistency
                    }
                }
            }
          // If the element is not on a boundary of the domain
          // we loop over his neighbors to compute the element
          // and neighbor boundary matrix contributions
          else
            {
              // Store a pointer to the neighbor we are currently
              // working on.
              const Elem* neighbor = elem->neighbor(side);

              // Get the global id of the element and the neighbor
              const unsigned int elem_id = elem->id();
              const unsigned int neighbor_id = neighbor->id();

              // If the neighbor has the same h level and is active
              // perform integration only if our global id is bigger than our neighbor id.
              // We don't want to compute twice the same contributions.
              // If the neighbor has a different h level perform integration
              // only if the neighbor is at a lower level.
              if ((neighbor->active() && (neighbor->level() == elem->level()) && (elem_id < neighbor_id)) || (neighbor->level() < elem->level()))
                {
                  // Pointer to the element side
                  AutoPtr<Elem> elem_side (elem->build_side(side));

                  // h dimension to compute the interior penalty penalty parameter
                  const unsigned int elem_b_order = static_cast<unsigned int>(fe_elem_face->get_order());
                  const unsigned int neighbor_b_order = static_cast<unsigned int>(fe_neighbor_face->get_order());
                  const double side_order = (elem_b_order + neighbor_b_order)/2.;
                  const double h_elem = (elem->volume()/elem_side->volume()) * 1./pow(side_order,2.);

                  // The quadrature point locations on the neighbor side
                  std::vector<Point> qface_neighbor_point;

                  // The quadrature point locations on the element side
                  std::vector<Point > qface_point;

                  // Reinitialize shape functions on the element side
                  fe_elem_face->reinit(elem, side);

                  // Get the physical locations of the element quadrature points
                  qface_point = fe_elem_face->get_xyz();

                  // Find their locations on the neighbor
                  unsigned int side_neighbor = neighbor->which_neighbor_am_i(elem);
                  if (refinement_type == "p")
                    fe_neighbor_face->side_map (neighbor, elem_side.get(), side_neighbor, qface.get_points(), qface_neighbor_point);
                  else
                    FEInterface::inverse_map (elem->dim(), fe->get_fe_type(), neighbor, qface_point, qface_neighbor_point);
                  // Calculate the neighbor element shape functions at those locations
                  fe_neighbor_face->reinit(neighbor, &qface_neighbor_point);

                  // Get the degree of freedom indices for the
                  // neighbor.  These define where in the global
                  // matrix this neighbor will contribute to.
                  std::vector<dof_id_type> neighbor_dof_indices;
                  dof_map.dof_indices (neighbor, neighbor_dof_indices);
                  const unsigned int n_neighbor_dofs = neighbor_dof_indices.size();

                  // Zero the element and neighbor side matrix before
                  // summing them.  We use the resize member here because
                  // the number of degrees of freedom might have changed from
                  // the last element or neighbor.
                  // Note that Kne and Ken are not square matrices if neighbor
                  // and element have a different p level
                  Kne.resize (n_neighbor_dofs, n_dofs);
                  Ken.resize (n_dofs, n_neighbor_dofs);
                  Kee.resize (n_dofs, n_dofs);
                  Knn.resize (n_neighbor_dofs, n_neighbor_dofs);

                  // Now we will build the element and neighbor
                  // boundary matrices.  This involves
                  // a double loop to integrate the test funcions
                  // (i) against the trial functions (j).
                  for (unsigned int qp=0; qp<qface.n_points(); qp++)
                    {
                      // Kee Matrix. Integrate the element test function i
                      // against the element test function j
                      for (unsigned int i=0; i<n_dofs; i++)
                        {
                          for (unsigned int j=0; j<n_dofs; j++)
                            {
                              Kee(i,j) -= 0.5 * JxW_face[qp] * (phi_face[j][qp]*(qface_normals[qp]*dphi_face[i][qp]) + phi_face[i][qp]*(qface_normals[qp]*dphi_face[j][qp])); // consistency
                              Kee(i,j) += JxW_face[qp] * penalty/h_elem * phi_face[j][qp]*phi_face[i][qp];  // stability
                            }
                        }

                      // Knn Matrix. Integrate the neighbor test function i
                      // against the neighbor test function j
                      for (unsigned int i=0; i<n_neighbor_dofs; i++)
                        {
                          for (unsigned int j=0; j<n_neighbor_dofs; j++)
                            {
                              Knn(i,j) += 0.5 * JxW_face[qp] * (phi_neighbor_face[j][qp]*(qface_normals[qp]*dphi_neighbor_face[i][qp]) + phi_neighbor_face[i][qp]*(qface_normals[qp]*dphi_neighbor_face[j][qp])); // consistency
                              Knn(i,j) += JxW_face[qp] * penalty/h_elem * phi_neighbor_face[j][qp]*phi_neighbor_face[i][qp]; // stability
                            }
                        }

                      // Kne Matrix. Integrate the neighbor test function i
                      // against the element test function j
                      for (unsigned int i=0; i<n_neighbor_dofs; i++)
                        {
                          for (unsigned int j=0; j<n_dofs; j++)
                            {
                              Kne(i,j) += 0.5 * JxW_face[qp] * (phi_neighbor_face[i][qp]*(qface_normals[qp]*dphi_face[j][qp]) - phi_face[j][qp]*(qface_normals[qp]*dphi_neighbor_face[i][qp])); // consistency
                              Kne(i,j) -= JxW_face[qp] * penalty/h_elem * phi_face[j][qp]*phi_neighbor_face[i][qp];  // stability
                            }
                        }

                      // Ken Matrix. Integrate the element test function i
                      // against the neighbor test function j
                      for (unsigned int i=0; i<n_dofs; i++)
                        {
                          for (unsigned int j=0; j<n_neighbor_dofs; j++)
                            {
                              Ken(i,j) += 0.5 * JxW_face[qp] * (phi_neighbor_face[j][qp]*(qface_normals[qp]*dphi_face[i][qp]) - phi_face[i][qp]*(qface_normals[qp]*dphi_neighbor_face[j][qp]));  // consistency
                              Ken(i,j) -= JxW_face[qp] * penalty/h_elem * phi_face[i][qp]*phi_neighbor_face[j][qp];  // stability
                            }
                        }
                    }

                  // The element and neighbor boundary matrix are now built
                  // for this side.  Add them to the global matrix
                  // The \p SparseMatrix::add_matrix() members do this for us.
                  ellipticdg_system.matrix->add_matrix(Kne,neighbor_dof_indices,dof_indices);
                  ellipticdg_system.matrix->add_matrix(Ken,dof_indices,neighbor_dof_indices);
                  ellipticdg_system.matrix->add_matrix(Kee,dof_indices);
                  ellipticdg_system.matrix->add_matrix(Knn,neighbor_dof_indices);
                }
            }
        }
      // The element interior matrix and right-hand-side are now built
      // for this element.  Add them to the global matrix and
      // right-hand-side vector.  The \p SparseMatrix::add_matrix()
      // and \p NumericVector::add_vector() members do this for us.
      ellipticdg_system.matrix->add_matrix(Ke, dof_indices);
      ellipticdg_system.rhs->add_vector(Fe, dof_indices);
    }

  std::cout << "done" << std::endl;
}
void LinearElasticityWithContact::residual_and_jacobian (const NumericVector<Number> & soln,
                                                         NumericVector<Number> * residual,
                                                         SparseMatrix<Number> * jacobian,
                                                         NonlinearImplicitSystem & /*sys*/)
{
  EquationSystems & es = _sys.get_equation_systems();
  const Real young_modulus = es.parameters.get<Real>("young_modulus");
  const Real poisson_ratio = es.parameters.get<Real>("poisson_ratio");

  const MeshBase & mesh = _sys.get_mesh();
  const unsigned int dim = mesh.mesh_dimension();

  const unsigned int u_var = _sys.variable_number ("u");

  DofMap & dof_map = _sys.get_dof_map();

  FEType fe_type = dof_map.variable_type(u_var);
  UniquePtr<FEBase> fe (FEBase::build(dim, fe_type));
  QGauss qrule (dim, fe_type.default_quadrature_order());
  fe->attach_quadrature_rule (&qrule);

  UniquePtr<FEBase> fe_face (FEBase::build(dim, fe_type));
  QGauss qface (dim-1, fe_type.default_quadrature_order());
  fe_face->attach_quadrature_rule (&qface);

  UniquePtr<FEBase> fe_neighbor_face (FEBase::build(dim, fe_type));
  fe_neighbor_face->attach_quadrature_rule (&qface);

  const std::vector<Real> & JxW = fe->get_JxW();
  const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi();

  if (jacobian)
    jacobian->zero();

  if (residual)
    residual->zero();

  // Do jacobian and residual assembly, including contact forces
  DenseVector<Number> Re;

  DenseSubVector<Number> Re_var[3] =
    {DenseSubVector<Number>(Re),
     DenseSubVector<Number>(Re),
     DenseSubVector<Number>(Re)};

  DenseMatrix<Number> Ke;
  DenseSubMatrix<Number> Ke_var[3][3] =
    {
      {DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke)},
      {DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke)},
      {DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke), DenseSubMatrix<Number>(Ke)}
    };

  std::vector<dof_id_type> dof_indices;
  std::vector< std::vector<dof_id_type> > dof_indices_var(3);

  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();

  for ( ; el != end_el; ++el)
    {
      const Elem * elem = *el;

      dof_map.dof_indices (elem, dof_indices);
      for (unsigned int var=0; var<3; var++)
        dof_map.dof_indices (elem, dof_indices_var[var], var);

      const unsigned int n_dofs   = dof_indices.size();
      const unsigned int n_var_dofs = dof_indices_var[0].size();

      fe->reinit (elem);

      Re.resize (n_dofs);
      for (unsigned int var=0; var<3; var++)
        Re_var[var].reposition (var*n_var_dofs, n_var_dofs);

      Ke.resize (n_dofs, n_dofs);
      for (unsigned int var_i=0; var_i<3; var_i++)
        for (unsigned int var_j=0; var_j<3; var_j++)
          Ke_var[var_i][var_j].reposition (var_i*n_var_dofs, var_j*n_var_dofs, n_var_dofs, n_var_dofs);

      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        {
          // Row is variable u, v, or w column is x, y, or z
          DenseMatrix<Number> grad_u(3, 3);
          for (unsigned int var_i=0; var_i<3; var_i++)
            for (unsigned int var_j=0; var_j<3; var_j++)
              for (unsigned int j=0; j<n_var_dofs; j++)
                grad_u(var_i, var_j) += dphi[j][qp](var_j)*soln(dof_indices_var[var_i][j]);

          // - C_ijkl u_k,l v_i,j
          for (unsigned int dof_i=0; dof_i<n_var_dofs; dof_i++)
            for (unsigned int i=0; i<3; i++)
              for (unsigned int j=0; j<3; j++)
                for (unsigned int k=0; k<3; k++)
                  for (unsigned int l=0; l<3; l++)
                    Re_var[i](dof_i) -= JxW[qp] *
                      elasticity_tensor(young_modulus, poisson_ratio, i, j, k, l) * grad_u(k,l) * dphi[dof_i][qp](j);

          // assemble \int_Omega C_ijkl u_k,l v_i,j \dx
          for (unsigned int dof_i=0; dof_i<n_var_dofs; dof_i++)
            for (unsigned int dof_j=0; dof_j<n_var_dofs; dof_j++)
              for (unsigned int i=0; i<3; i++)
                for (unsigned int j=0; j<3; j++)
                  for (unsigned int k=0; k<3; k++)
                    for (unsigned int l=0; l<3; l++)
                      Ke_var[i][k](dof_i, dof_j) -= JxW[qp] *
                        elasticity_tensor(young_modulus, poisson_ratio, i, j, k, l) * dphi[dof_j][qp](l) * dphi[dof_i][qp](j);
        }

      dof_map.constrain_element_matrix_and_vector (Ke, Re, dof_indices);

      if (jacobian)
        jacobian->add_matrix (Ke, dof_indices);

      if (residual)
        residual->add_vector (Re, dof_indices);
    }

  // Move a copy of the mesh based on the solution. This is used to get
  // the contact forces.
  UniquePtr<MeshBase> mesh_clone = mesh.clone();
  move_mesh(*mesh_clone, soln);

  // Add contributions due to contact penalty forces. Only need to do this on
  // one processor.
  _lambda_plus_penalty_values.clear();

  LIBMESH_BEST_UNORDERED_MAP<dof_id_type, dof_id_type>::iterator it =
    _augment_sparsity._contact_node_map.begin();
  LIBMESH_BEST_UNORDERED_MAP<dof_id_type, dof_id_type>::iterator it_end =
    _augment_sparsity._contact_node_map.end();

  for ( ; it != it_end; ++it)
    {
      dof_id_type lower_point_id = it->first;
      dof_id_type upper_point_id = it->second;

      Point upper_to_lower;
      {
        Point lower_node_moved = mesh_clone->point(lower_point_id);
        Point upper_node_moved = mesh_clone->point(upper_point_id);

        upper_to_lower = (lower_node_moved - upper_node_moved);
      }

      // Set the contact force direction. Usually this would be calculated
      // separately on each master node, but here we just set it to (0, 0, 1)
      // everywhere.
      Point contact_force_direction(0., 0., 1.);

      // gap_function > 0. means that contact has been detected
      // gap_function < 0. means that we have a gap
      // This sign convention matches Simo & Laursen (1992).
      Real gap_function = upper_to_lower * contact_force_direction;

      // We use the sign of lambda_plus_penalty to determine whether or
      // not we need to impose a contact force.
      Real lambda_plus_penalty =
        (_lambdas[upper_point_id] + gap_function * _contact_penalty);

      if (lambda_plus_penalty < 0.)
        lambda_plus_penalty = 0.;

      // Store lambda_plus_penalty, we'll need to use it later to update _lambdas
      _lambda_plus_penalty_values[upper_point_id] = lambda_plus_penalty;

      const Node & lower_node = mesh.node_ref(lower_point_id);
      const Node & upper_node = mesh.node_ref(upper_point_id);

      std::vector<dof_id_type> dof_indices_on_lower_node(3);
      std::vector<dof_id_type> dof_indices_on_upper_node(3);
      DenseVector<Number> lower_contact_force_vec(3);
      DenseVector<Number> upper_contact_force_vec(3);

      for (unsigned int var=0; var<3; var++)
        {
          dof_indices_on_lower_node[var] = lower_node.dof_number(_sys.number(), var, 0);
          lower_contact_force_vec(var) = -lambda_plus_penalty * contact_force_direction(var);

          dof_indices_on_upper_node[var] = upper_node.dof_number(_sys.number(), var, 0);
          upper_contact_force_vec(var) = lambda_plus_penalty * contact_force_direction(var);
        }

      if (lambda_plus_penalty > 0.)
        {
          if (residual && (_sys.comm().rank() == 0))
            {
              residual->add_vector (lower_contact_force_vec, dof_indices_on_lower_node);
              residual->add_vector (upper_contact_force_vec, dof_indices_on_upper_node);
            }

          // Add the Jacobian terms due to the contact forces. The lambda contribution
          // is not relevant here because it doesn't depend on the solution.
          if (jacobian && (_sys.comm().rank() == 0))
            for (unsigned int var=0; var<3; var++)
              for (unsigned int j=0; j<3; j++)
                {
                  jacobian->add(dof_indices_on_lower_node[var],
                                dof_indices_on_upper_node[j],
                                _contact_penalty * contact_force_direction(j) * contact_force_direction(var));

                  jacobian->add(dof_indices_on_lower_node[var],
                                dof_indices_on_lower_node[j],
                                -_contact_penalty * contact_force_direction(j) * contact_force_direction(var));

                  jacobian->add(dof_indices_on_upper_node[var],
                                dof_indices_on_lower_node[j],
                                _contact_penalty * contact_force_direction(j) * contact_force_direction(var));

                  jacobian->add(dof_indices_on_upper_node[var],
                                dof_indices_on_upper_node[j],
                                -_contact_penalty * contact_force_direction(j) * contact_force_direction(var));
                }
        }
      else
        {
          // We add zeros to the matrix even when lambda_plus_penalty = 0.
          // We do this because some linear algebra libraries (e.g. PETSc)
          // will condense out any unused entries from the sparsity pattern,
          // so adding these zeros in ensures that these entries are not
          // condensed out.
          if (jacobian && (_sys.comm().rank() == 0))
            for (unsigned int var=0; var<3; var++)
              for (unsigned int j=0; j<3; j++)
                {
                  jacobian->add(dof_indices_on_lower_node[var],
                                dof_indices_on_upper_node[j],
                                0.);

                  jacobian->add(dof_indices_on_lower_node[var],
                                dof_indices_on_lower_node[j],
                                0.);

                  jacobian->add(dof_indices_on_upper_node[var],
                                dof_indices_on_lower_node[j],
                                0.);

                  jacobian->add(dof_indices_on_upper_node[var],
                                dof_indices_on_upper_node[j],
                                0.);
                }
        }
    }
}
Beispiel #20
0
// We now define the matrix assembly function for the
// Biharmonic system.  We need to first compute element
// matrices and right-hand sides, and then take into
// account the boundary conditions, which will be handled
// via a penalty method.
void assemble_biharmonic(EquationSystems& es,
                      const std::string& system_name)
{
#ifdef LIBMESH_ENABLE_AMR
#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES

  // It is a good idea to make sure we are assembling
  // the proper system.
  libmesh_assert_equal_to (system_name, "Biharmonic");

  // Declare a performance log.  Give it a descriptive
  // string to identify what part of the code we are
  // logging, since there may be many PerfLogs in an
  // application.
  PerfLog perf_log ("Matrix Assembly",false);
  
    // Get a constant reference to the mesh object.
  const MeshBase& mesh = es.get_mesh();

  // The dimension that we are running
  const unsigned int dim = mesh.mesh_dimension();

  // Get a reference to the LinearImplicitSystem we are solving
  LinearImplicitSystem& system = es.get_system<LinearImplicitSystem>("Biharmonic");
  
  // A reference to the \p DofMap object for this system.  The \p DofMap
  // object handles the index translation from node and element numbers
  // to degree of freedom numbers.  We will talk more about the \p DofMap
  // in future examples.
  const DofMap& dof_map = system.get_dof_map();

  // Get a constant reference to the Finite Element type
  // for the first (and only) variable in the system.
  FEType fe_type = dof_map.variable_type(0);

  // Build a Finite Element object of the specified type.  Since the
  // \p FEBase::build() member dynamically creates memory we will
  // store the object as an \p AutoPtr<FEBase>.  This can be thought
  // of as a pointer that will clean up after itself.
  AutoPtr<FEBase> fe (FEBase::build(dim, fe_type));
  
  // Quadrature rule for numerical integration.
  // With 2D triangles, the Clough quadrature rule puts a Gaussian
  // quadrature rule on each of the 3 subelements
  AutoPtr<QBase> qrule(fe_type.default_quadrature_rule(dim));

  // Tell the finite element object to use our quadrature rule.
  fe->attach_quadrature_rule (qrule.get());

  // Declare a special finite element object for
  // boundary integration.
  AutoPtr<FEBase> fe_face (FEBase::build(dim, fe_type));
              
  // Boundary integration requires another quadraure rule,
  // with dimensionality one less than the dimensionality
  // of the element.
  // In 1D, the Clough and Gauss quadrature rules are identical.
  AutoPtr<QBase> qface(fe_type.default_quadrature_rule(dim-1));

  // Tell the finte element object to use our
  // quadrature rule.
  fe_face->attach_quadrature_rule (qface.get());

  // Here we define some references to cell-specific data that
  // will be used to assemble the linear system.
  // We begin with the element Jacobian * quadrature weight at each
  // integration point.   
  const std::vector<Real>& JxW = fe->get_JxW();

  // The physical XY locations of the quadrature points on the element.
  // These might be useful for evaluating spatially varying material
  // properties at the quadrature points.
  const std::vector<Point>& q_point = fe->get_xyz();

  // The element shape functions evaluated at the quadrature points.
  const std::vector<std::vector<Real> >& phi = fe->get_phi();

  // The element shape function second derivatives evaluated at the
  // quadrature points.  Note that for the simple biharmonic, shape
  // function first derivatives are unnecessary.
  const std::vector<std::vector<RealTensor> >& d2phi = fe->get_d2phi();

  // For efficiency we will compute shape function laplacians n times,
  // not n^2
  std::vector<Real> shape_laplacian;

  // Define data structures to contain the element matrix
  // and right-hand-side vector contribution.  Following
  // basic finite element terminology we will denote these
  // "Ke" and "Fe". More detail is in example 3.
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  // This vector will hold the degree of freedom indices for
  // the element.  These define where in the global system
  // the element degrees of freedom get mapped.
  std::vector<unsigned int> dof_indices;

  // Now we will loop over all the elements in the mesh.  We will
  // compute the element matrix and right-hand-side contribution.  See
  // example 3 for a discussion of the element iterators.

  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end(); 
  
  for ( ; el != end_el; ++el)
    {
      // Start logging the shape function initialization.
      // This is done through a simple function call with
      // the name of the event to log.
      perf_log.push("elem init");      

      // Store a pointer to the element we are currently
      // working on.  This allows for nicer syntax later.
      const Elem* elem = *el;

      // Get the degree of freedom indices for the
      // current element.  These define where in the global
      // matrix and right-hand-side this element will
      // contribute to.
      dof_map.dof_indices (elem, dof_indices);

      // Compute the element-specific data for the current
      // element.  This involves computing the location of the
      // quadrature points (q_point) and the shape functions
      // (phi, dphi) for the current element.
      fe->reinit (elem);

      // Zero the element matrix and right-hand side before
      // summing them.
      Ke.resize (dof_indices.size(),
                 dof_indices.size());

      Fe.resize (dof_indices.size());

      // Make sure there is enough room in this cache
      shape_laplacian.resize(dof_indices.size());

      // Stop logging the shape function initialization.
      // If you forget to stop logging an event the PerfLog
      // object will probably catch the error and abort.
      perf_log.pop("elem init");      

      // Now we will build the element matrix.  This involves
      // a double loop to integrate laplacians of the test funcions
      // (i) against laplacians of the trial functions (j).
      //
      // This step is why we need the Clough-Tocher elements -
      // these C1 differentiable elements have square-integrable
      // second derivatives.
      //
      // Now start logging the element matrix computation
      perf_log.push ("Ke");

      for (unsigned int qp=0; qp<qrule->n_points(); qp++)
        {
          for (unsigned int i=0; i<phi.size(); i++)
            {
              shape_laplacian[i] = d2phi[i][qp](0,0)+d2phi[i][qp](1,1);
              if (dim == 3)
                 shape_laplacian[i] += d2phi[i][qp](2,2);
            }
          for (unsigned int i=0; i<phi.size(); i++)
            for (unsigned int j=0; j<phi.size(); j++)
              Ke(i,j) += JxW[qp]*
                         shape_laplacian[i]*shape_laplacian[j];
        }

      // Stop logging the matrix computation
      perf_log.pop ("Ke");


      // At this point the interior element integration has
      // been completed.  However, we have not yet addressed
      // boundary conditions.  For this example we will only
      // consider simple Dirichlet boundary conditions imposed
      // via the penalty method.  Note that this is a fourth-order
      // problem: Dirichlet boundary conditions include *both*
      // boundary values and boundary normal fluxes.
      {
        // Start logging the boundary condition computation
        perf_log.push ("BCs");

        // The penalty values, for solution boundary trace and flux.  
        const Real penalty = 1e10;
        const Real penalty2 = 1e10;

        // The following loops over the sides of the element.
        // If the element has no neighbor on a side then that
        // side MUST live on a boundary of the domain.
        for (unsigned int s=0; s<elem->n_sides(); s++)
          if (elem->neighbor(s) == NULL)
            {
              // The value of the shape functions at the quadrature
              // points.
              const std::vector<std::vector<Real> >&  phi_face =
                              fe_face->get_phi();

              // The value of the shape function derivatives at the
              // quadrature points.
              const std::vector<std::vector<RealGradient> >& dphi_face =
                              fe_face->get_dphi();

              // The Jacobian * Quadrature Weight at the quadrature
              // points on the face.
              const std::vector<Real>& JxW_face = fe_face->get_JxW();
                                                                               
              // The XYZ locations (in physical space) of the
              // quadrature points on the face.  This is where
              // we will interpolate the boundary value function.
              const std::vector<Point>& qface_point = fe_face->get_xyz();

              const std::vector<Point>& face_normals =
                              fe_face->get_normals();

              // Compute the shape function values on the element
              // face.
              fe_face->reinit(elem, s);
                                                                                
              // Loop over the face quagrature points for integration.
              for (unsigned int qp=0; qp<qface->n_points(); qp++)
                {
                  // The boundary value.
                  Number value = exact_solution(qface_point[qp],
                                                es.parameters, "null",
                                                "void");
                  Gradient flux = exact_2D_derivative(qface_point[qp],
                                                      es.parameters,
                                                      "null", "void");

                  // Matrix contribution of the L2 projection.
                  // Note that the basis function values are
                  // integrated against test function values while
                  // basis fluxes are integrated against test function
                  // fluxes.
                  for (unsigned int i=0; i<phi_face.size(); i++)
                    for (unsigned int j=0; j<phi_face.size(); j++)
                      Ke(i,j) += JxW_face[qp] *
                                 (penalty * phi_face[i][qp] *
                                  phi_face[j][qp] + penalty2
                                  * (dphi_face[i][qp] *
                                  face_normals[qp]) *
                                  (dphi_face[j][qp] *
                                   face_normals[qp]));

                  // Right-hand-side contribution of the L2
                  // projection.
                  for (unsigned int i=0; i<phi_face.size(); i++)
                    Fe(i) += JxW_face[qp] *
                                    (penalty * value * phi_face[i][qp]
                                     + penalty2 * 
                                     (flux * face_normals[qp])
                                    * (dphi_face[i][qp]
                                       * face_normals[qp]));

                }
            } 
        
        // Stop logging the boundary condition computation
        perf_log.pop ("BCs");
      } 

      for (unsigned int qp=0; qp<qrule->n_points(); qp++)
        for (unsigned int i=0; i<phi.size(); i++)
          Fe(i) += JxW[qp]*phi[i][qp]*forcing_function(q_point[qp]);

      // The element matrix and right-hand-side are now built
      // for this element.  Add them to the global matrix and
      // right-hand-side vector.  The \p SparseMatrix::add_matrix()
      // and \p NumericVector::add_vector() members do this for us.
      // Start logging the insertion of the local (element)
      // matrix and vector into the global matrix and vector
      perf_log.push ("matrix insertion");

      dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices);
      system.matrix->add_matrix (Ke, dof_indices);
      system.rhs->add_vector    (Fe, dof_indices);

      // Stop logging the insertion of the local (element)
      // matrix and vector into the global matrix and vector
      perf_log.pop ("matrix insertion");
    }

  // That's it.  We don't need to do anything else to the
  // PerfLog.  When it goes out of scope (at this function return)
  // it will print its log to the screen. Pretty easy, huh?

#else

#endif // #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
#endif // #ifdef LIBMESH_ENABLE_AMR
}
// We now define the matrix assembly function for the
// Poisson system.  We need to first compute element
// matrices and right-hand sides, and then take into
// account the boundary conditions.
void assemble_poisson(EquationSystems& es,
                      const std::string& system_name)
{
  // It is a good idea to make sure we are assembling
  // the proper system.
  libmesh_assert_equal_to (system_name, "Poisson");


  // Declare a performance log.  Give it a descriptive
  // string to identify what part of the code we are
  // logging, since there may be many PerfLogs in an
  // application.
  PerfLog perf_log ("Matrix Assembly");

  // Get a constant reference to the mesh object.
  const MeshBase& mesh = es.get_mesh();

  // The dimension that we are running
  const unsigned int dim = mesh.mesh_dimension();

  // Get a reference to the LinearImplicitSystem we are solving
  LinearImplicitSystem& system = es.get_system<LinearImplicitSystem>("Poisson");

  // A reference to the \p DofMap object for this system.  The \p DofMap
  // object handles the index translation from node and element numbers
  // to degree of freedom numbers.  We will talk more about the \p DofMap
  // in future examples.
  const DofMap& dof_map = system.get_dof_map();

  // Get a constant reference to the Finite Element type
  // for the first (and only) variable in the system.
  FEType fe_type = dof_map.variable_type(0);

  // Build a Finite Element object of the specified type.  Since the
  // \p FEBase::build() member dynamically creates memory we will
  // store the object as an \p UniquePtr<FEBase>.  This can be thought
  // of as a pointer that will clean up after itself.
  UniquePtr<FEBase> fe (FEBase::build(dim, fe_type));

  // A 5th order Gauss quadrature rule for numerical integration.
  QGauss qrule (dim, FIFTH);

  // Tell the finite element object to use our quadrature rule.
  fe->attach_quadrature_rule (&qrule);

  // Declare a special finite element object for
  // boundary integration.
  UniquePtr<FEBase> fe_face (FEBase::build(dim, fe_type));

  // Boundary integration requires one quadraure rule,
  // with dimensionality one less than the dimensionality
  // of the element.
  QGauss qface(dim-1, FIFTH);

  // Tell the finte element object to use our
  // quadrature rule.
  fe_face->attach_quadrature_rule (&qface);

  // Here we define some references to cell-specific data that
  // will be used to assemble the linear system.
  // We begin with the element Jacobian * quadrature weight at each
  // integration point.
  const std::vector<Real>& JxW = fe->get_JxW();

  // The physical XY locations of the quadrature points on the element.
  // These might be useful for evaluating spatially varying material
  // properties at the quadrature points.
  const std::vector<Point>& q_point = fe->get_xyz();

  // The element shape functions evaluated at the quadrature points.
  const std::vector<std::vector<Real> >& phi = fe->get_phi();

  // The element shape function gradients evaluated at the quadrature
  // points.
  const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi();

  // Define data structures to contain the element matrix
  // and right-hand-side vector contribution.  Following
  // basic finite element terminology we will denote these
  // "Ke" and "Fe". More detail is in example 3.
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  // This vector will hold the degree of freedom indices for
  // the element.  These define where in the global system
  // the element degrees of freedom get mapped.
  std::vector<dof_id_type> dof_indices;

  // Now we will loop over all the elements in the mesh.
  // We will compute the element matrix and right-hand-side
  // contribution.  See example 3 for a discussion of the
  // element iterators.
  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();

  for ( ; el != end_el; ++el)
    {
      // Start logging the shape function initialization.
      // This is done through a simple function call with
      // the name of the event to log.
      perf_log.push("elem init");

      // Store a pointer to the element we are currently
      // working on.  This allows for nicer syntax later.
      const Elem* elem = *el;

      // Get the degree of freedom indices for the
      // current element.  These define where in the global
      // matrix and right-hand-side this element will
      // contribute to.
      dof_map.dof_indices (elem, dof_indices);

      // Compute the element-specific data for the current
      // element.  This involves computing the location of the
      // quadrature points (q_point) and the shape functions
      // (phi, dphi) for the current element.
      fe->reinit (elem);

      // Zero the element matrix and right-hand side before
      // summing them.  We use the resize member here because
      // the number of degrees of freedom might have changed from
      // the last element.  Note that this will be the case if the
      // element type is different (i.e. the last element was a
      // triangle, now we are on a quadrilateral).
      Ke.resize (dof_indices.size(),
                 dof_indices.size());

      Fe.resize (dof_indices.size());

      // Stop logging the shape function initialization.
      // If you forget to stop logging an event the PerfLog
      // object will probably catch the error and abort.
      perf_log.pop("elem init");

      // Now we will build the element matrix.  This involves
      // a double loop to integrate the test funcions (i) against
      // the trial functions (j).
      //
      // We have split the numeric integration into two loops
      // so that we can log the matrix and right-hand-side
      // computation seperately.
      //
      // Now start logging the element matrix computation
      perf_log.push ("Ke");

      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        for (unsigned int i=0; i<phi.size(); i++)
          for (unsigned int j=0; j<phi.size(); j++)
            Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]);


      // Stop logging the matrix computation
      perf_log.pop ("Ke");

      // Now we build the element right-hand-side contribution.
      // This involves a single loop in which we integrate the
      // "forcing function" in the PDE against the test functions.
      //
      // Start logging the right-hand-side computation
      perf_log.push ("Fe");

      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        {
          // fxy is the forcing function for the Poisson equation.
          // In this case we set fxy to be a finite difference
          // Laplacian approximation to the (known) exact solution.
          //
          // We will use the second-order accurate FD Laplacian
          // approximation, which in 2D on a structured grid is
          //
          // u_xx + u_yy = (u(i-1,j) + u(i+1,j) +
          //                u(i,j-1) + u(i,j+1) +
          //                -4*u(i,j))/h^2
          //
          // Since the value of the forcing function depends only
          // on the location of the quadrature point (q_point[qp])
          // we will compute it here, outside of the i-loop
          const Real x = q_point[qp](0);
#if LIBMESH_DIM > 1
          const Real y = q_point[qp](1);
#else
          const Real y = 0.;
#endif
#if LIBMESH_DIM > 2
          const Real z = q_point[qp](2);
#else
          const Real z = 0.;
#endif
          const Real eps = 1.e-3;

          const Real uxx = (exact_solution(x-eps,y,z) +
                            exact_solution(x+eps,y,z) +
                            -2.*exact_solution(x,y,z))/eps/eps;

          const Real uyy = (exact_solution(x,y-eps,z) +
                            exact_solution(x,y+eps,z) +
                            -2.*exact_solution(x,y,z))/eps/eps;

          const Real uzz = (exact_solution(x,y,z-eps) +
                            exact_solution(x,y,z+eps) +
                            -2.*exact_solution(x,y,z))/eps/eps;

          Real fxy;
          if(dim==1)
            {
              // In 1D, compute the rhs by differentiating the
              // exact solution twice.
              const Real pi = libMesh::pi;
              fxy = (0.25*pi*pi)*sin(.5*pi*x);
            }
          else
            {
              fxy = - (uxx + uyy + ((dim==2) ? 0. : uzz));
            }

          // Add the RHS contribution
          for (unsigned int i=0; i<phi.size(); i++)
            Fe(i) += JxW[qp]*fxy*phi[i][qp];
        }

      // Stop logging the right-hand-side computation
      perf_log.pop ("Fe");

      // If this assembly program were to be used on an adaptive mesh,
      // we would have to apply any hanging node constraint equations
      // Also, note that here we call heterogenously_constrain_element_matrix_and_vector
      // to impose a inhomogeneous Dirichlet boundary conditions.
      dof_map.heterogenously_constrain_element_matrix_and_vector (Ke, Fe, dof_indices);

      // The element matrix and right-hand-side are now built
      // for this element.  Add them to the global matrix and
      // right-hand-side vector.  The \p SparseMatrix::add_matrix()
      // and \p NumericVector::add_vector() members do this for us.
      // Start logging the insertion of the local (element)
      // matrix and vector into the global matrix and vector
      perf_log.push ("matrix insertion");

      system.matrix->add_matrix (Ke, dof_indices);
      system.rhs->add_vector    (Fe, dof_indices);

      // Start logging the insertion of the local (element)
      // matrix and vector into the global matrix and vector
      perf_log.pop ("matrix insertion");
    }

  // That's it.  We don't need to do anything else to the
  // PerfLog.  When it goes out of scope (at this function return)
  // it will print its log to the screen. Pretty easy, huh?
}
Beispiel #22
0
void assemble_poisson(EquationSystems & es,
                      const ElementIdMap & lower_to_upper)
{
  const MeshBase & mesh = es.get_mesh();
  const unsigned int dim = mesh.mesh_dimension();

  Real R = es.parameters.get<Real>("R");

  LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>("Poisson");

  const DofMap & dof_map = system.get_dof_map();

  FEType fe_type = dof_map.variable_type(0);

  UniquePtr<FEBase> fe (FEBase::build(dim, fe_type));
  UniquePtr<FEBase> fe_elem_face (FEBase::build(dim, fe_type));
  UniquePtr<FEBase> fe_neighbor_face (FEBase::build(dim, fe_type));

  QGauss qrule (dim, fe_type.default_quadrature_order());
  QGauss qface(dim-1, fe_type.default_quadrature_order());

  fe->attach_quadrature_rule (&qrule);
  fe_elem_face->attach_quadrature_rule (&qface);
  fe_neighbor_face->attach_quadrature_rule (&qface);

  const std::vector<Real> & JxW = fe->get_JxW();
  const std::vector<std::vector<Real> > & phi = fe->get_phi();
  const std::vector<std::vector<RealGradient> > & dphi = fe->get_dphi();

  const std::vector<Real> & JxW_face = fe_elem_face->get_JxW();

  const std::vector<Point> & qface_points = fe_elem_face->get_xyz();

  const std::vector<std::vector<Real> > & phi_face          = fe_elem_face->get_phi();
  const std::vector<std::vector<Real> > & phi_neighbor_face = fe_neighbor_face->get_phi();

  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  DenseMatrix<Number> Kne;
  DenseMatrix<Number> Ken;
  DenseMatrix<Number> Kee;
  DenseMatrix<Number> Knn;

  std::vector<dof_id_type> dof_indices;

  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();

  for ( ; el != end_el; ++el)
    {
      const Elem * elem = *el;

      dof_map.dof_indices (elem, dof_indices);
      const unsigned int n_dofs = dof_indices.size();

      fe->reinit (elem);

      Ke.resize (n_dofs, n_dofs);
      Fe.resize (n_dofs);

      // Assemble element interior terms for the matrix
      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        for (unsigned int i=0; i<n_dofs; i++)
          for (unsigned int j=0; j<n_dofs; j++)
            Ke(i,j) += JxW[qp]*(dphi[i][qp]*dphi[j][qp]);

      // Boundary flux provides forcing in this example
      {
        for (unsigned int side=0; side<elem->n_sides(); side++)
          if (elem->neighbor(side) == NULL)
            {
              if (mesh.get_boundary_info().has_boundary_id (elem, side, MIN_Z_BOUNDARY))
                {
                  fe_elem_face->reinit(elem, side);

                  for (unsigned int qp=0; qp<qface.n_points(); qp++)
                    for (unsigned int i=0; i<phi.size(); i++)
                      Fe(i) += JxW_face[qp] * phi_face[i][qp];
                }

            }
      }

      // Add boundary terms on the crack
      {
        for (unsigned int side=0; side<elem->n_sides(); side++)
          if (elem->neighbor(side) == NULL)
            {
              // Found the lower side of the crack. Assemble terms due to lower and upper in here.
              if (mesh.get_boundary_info().has_boundary_id (elem, side, CRACK_BOUNDARY_LOWER))
                {
                  fe_elem_face->reinit(elem, side);

                  ElementIdMap::const_iterator ltu_it =
                    lower_to_upper.find(std::make_pair(elem->id(), side));
                  dof_id_type upper_elem_id = ltu_it->second;
                  const Elem * neighbor = mesh.elem(upper_elem_id);

                  std::vector<Point> qface_neighbor_points;
                  FEInterface::inverse_map (elem->dim(), fe->get_fe_type(),
                                            neighbor, qface_points, qface_neighbor_points);
                  fe_neighbor_face->reinit(neighbor, &qface_neighbor_points);

                  std::vector<dof_id_type> neighbor_dof_indices;
                  dof_map.dof_indices (neighbor, neighbor_dof_indices);
                  const unsigned int n_neighbor_dofs = neighbor_dof_indices.size();

                  Kne.resize (n_neighbor_dofs, n_dofs);
                  Ken.resize (n_dofs, n_neighbor_dofs);
                  Kee.resize (n_dofs, n_dofs);
                  Knn.resize (n_neighbor_dofs, n_neighbor_dofs);

                  // Lower-to-lower coupling term
                  for (unsigned int qp=0; qp<qface.n_points(); qp++)
                    for (unsigned int i=0; i<n_dofs; i++)
                      for (unsigned int j=0; j<n_dofs; j++)
                        Kee(i,j) -= JxW_face[qp] * (1./R)*(phi_face[i][qp] * phi_face[j][qp]);

                  // Lower-to-upper coupling term
                  for (unsigned int qp=0; qp<qface.n_points(); qp++)
                    for (unsigned int i=0; i<n_dofs; i++)
                      for (unsigned int j=0; j<n_neighbor_dofs; j++)
                        Ken(i,j) += JxW_face[qp] * (1./R)*(phi_face[i][qp] * phi_neighbor_face[j][qp]);

                  // Upper-to-upper coupling term
                  for (unsigned int qp=0; qp<qface.n_points(); qp++)
                    for (unsigned int i=0; i<n_neighbor_dofs; i++)
                      for (unsigned int j=0; j<n_neighbor_dofs; j++)
                        Knn(i,j) -= JxW_face[qp] * (1./R)*(phi_neighbor_face[i][qp] * phi_neighbor_face[j][qp]);

                  // Upper-to-lower coupling term
                  for (unsigned int qp=0; qp<qface.n_points(); qp++)
                    for (unsigned int i=0; i<n_neighbor_dofs; i++)
                      for (unsigned int j=0; j<n_dofs; j++)
                        Kne(i,j) += JxW_face[qp] * (1./R)*(phi_neighbor_face[i][qp] * phi_face[j][qp]);

                  system.matrix->add_matrix(Kne, neighbor_dof_indices, dof_indices);
                  system.matrix->add_matrix(Ken, dof_indices, neighbor_dof_indices);
                  system.matrix->add_matrix(Kee, dof_indices);
                  system.matrix->add_matrix(Knn, neighbor_dof_indices);
                }
            }
      }

      dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices);

      system.matrix->add_matrix (Ke, dof_indices);
      system.rhs->add_vector    (Fe, dof_indices);
    }
}
void
MultiAppProjectionTransfer::assembleL2From(EquationSystems & es, const std::string & system_name)
{
  unsigned int n_apps = _multi_app->numGlobalApps();
  std::vector<NumericVector<Number> *> from_slns(n_apps, NULL);
  std::vector<MeshFunction *> from_fns(n_apps, NULL);
  std::vector<MeshTools::BoundingBox *> from_bbs(n_apps, NULL);

  // get bounding box, mesh function and solution for each subapp
  for (unsigned int i = 0; i < n_apps; i++)
  {
    if (!_multi_app->hasLocalApp(i))
      continue;

    MPI_Comm swapped = Moose::swapLibMeshComm(_multi_app->comm());

    FEProblem & from_problem = *_multi_app->appProblem(i);
    EquationSystems & from_es = from_problem.es();
    MeshBase & from_mesh = from_es.get_mesh();
    MeshTools::BoundingBox * app_box = new MeshTools::BoundingBox(MeshTools::processor_bounding_box(from_mesh, from_mesh.processor_id()));
    from_bbs[i] = app_box;

    MooseVariable & from_var = from_problem.getVariable(0, _from_var_name);
    System & from_sys = from_var.sys().system();
    unsigned int from_var_num = from_sys.variable_number(from_var.name());

    NumericVector<Number> * serialized_from_solution = NumericVector<Number>::build(from_sys.comm()).release();
    serialized_from_solution->init(from_sys.n_dofs(), false, SERIAL);
    // Need to pull down a full copy of this vector on every processor so we can get values in parallel
    from_sys.solution->localize(*serialized_from_solution);
    from_slns[i] = serialized_from_solution;

    MeshFunction * from_func = new MeshFunction(from_es, *serialized_from_solution, from_sys.get_dof_map(), from_var_num);
    from_func->init(Trees::ELEMENTS);
    from_func->enable_out_of_mesh_mode(NOTFOUND);
    from_fns[i] = from_func;

    Moose::swapLibMeshComm(swapped);
  }


  const MeshBase& mesh = es.get_mesh();
  const unsigned int dim = mesh.mesh_dimension();

  LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>(system_name);

  FEType fe_type = system.variable_type(0);
  AutoPtr<FEBase> fe(FEBase::build(dim, fe_type));
  QGauss qrule(dim, fe_type.default_quadrature_order());
  fe->attach_quadrature_rule(&qrule);
  const std::vector<Real> & JxW = fe->get_JxW();
  const std::vector<std::vector<Real> > & phi = fe->get_phi();
  const std::vector<Point> & xyz = fe->get_xyz();

  const DofMap& dof_map = system.get_dof_map();
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;
  std::vector<dof_id_type> dof_indices;

  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();
  for ( ; el != end_el; ++el)
  {
    const Elem* elem = *el;

    fe->reinit (elem);

    dof_map.dof_indices (elem, dof_indices);
    Ke.resize (dof_indices.size(), dof_indices.size());
    Fe.resize (dof_indices.size());

    for (unsigned int qp = 0; qp < qrule.n_points(); qp++)
    {
      Point qpt = xyz[qp];
      Real f = 0.;
      for (unsigned int app = 0; app < n_apps; app++)
      {
        Point pt = qpt - _multi_app->position(app);
        if (from_bbs[app] != NULL && from_bbs[app]->contains_point(pt))
        {
          MPI_Comm swapped = Moose::swapLibMeshComm(_multi_app->comm());
          f = (*from_fns[app])(pt);
          Moose::swapLibMeshComm(swapped);
          break;
        }
      }

      // Now compute the element matrix and RHS contributions.
      for (unsigned int i=0; i<phi.size(); i++)
      {
        // RHS
        Fe(i) += JxW[qp] * (f * phi[i][qp]);

        if (_compute_matrix)
          for (unsigned int j = 0; j < phi.size(); j++)
          {
            // The matrix contribution
            Ke(i,j) += JxW[qp] * (phi[i][qp] * phi[j][qp]);
          }
      }
      dof_map.constrain_element_matrix_and_vector(Ke, Fe, dof_indices);

      if (_compute_matrix)
        system.matrix->add_matrix(Ke, dof_indices);
      system.rhs->add_vector(Fe, dof_indices);
    }
  }

  for (unsigned int i = 0; i < n_apps; i++)
  {
    delete from_fns[i];
    delete from_bbs[i];
    delete from_slns[i];
  }
}
Beispiel #24
0
// 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
}
Beispiel #25
0
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);
    }
}
Beispiel #26
0
void assemble(EquationSystems& es,
              const std::string& libmesh_dbg_var(system_name))
{
  libmesh_assert_equal_to (system_name, "primary");

  const MeshBase& mesh   = es.get_mesh();
  const unsigned int dim = mesh.mesh_dimension();

  // Also use a 3x3x3 quadrature rule (3D).  Then tell the FE
  // about the geometry of the problem and the quadrature rule
  FEType fe_type (FIRST);

  UniquePtr<FEBase> fe(FEBase::build(dim, fe_type));
  QGauss qrule(dim, FIFTH);

  fe->attach_quadrature_rule (&qrule);

  UniquePtr<FEBase> fe_face(FEBase::build(dim, fe_type));
  QGauss qface(dim-1, FIFTH);

  fe_face->attach_quadrature_rule(&qface);

  LinearImplicitSystem& system =
    es.get_system<LinearImplicitSystem>("primary");


  // These are references to cell-specific data
  const std::vector<Real>& JxW_face                   = fe_face->get_JxW();
  const std::vector<Real>& JxW                        = fe->get_JxW();
  const std::vector<Point>& q_point                   = fe->get_xyz();
  const std::vector<std::vector<Real> >& phi          = fe->get_phi();
  const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi();

  std::vector<unsigned int> dof_indices_U;
  std::vector<unsigned int> dof_indices_V;
  const DofMap& dof_map = system.get_dof_map();

  DenseMatrix<Number> Kuu;
  DenseMatrix<Number> Kvv;
  DenseVector<Number> Fu;
  DenseVector<Number> Fv;

  Real vol=0., area=0.;

  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();

  for (; el != end_el; ++el)
    {
      const Elem* elem = *el;

      // recompute the element-specific data for the current element
      fe->reinit (elem);


      //fe->print_info();

      dof_map.dof_indices(elem, dof_indices_U, 0);
      dof_map.dof_indices(elem, dof_indices_V, 1);

      // zero the element matrix and vector
      Kuu.resize (phi.size(),
                  phi.size());

      Kvv.resize (phi.size(),
                  phi.size());

      Fu.resize (phi.size());
      Fv.resize (phi.size());

      // standard stuff...  like in code 1.
      for (unsigned int gp=0; gp<qrule.n_points(); gp++)
        {
          for (unsigned int i=0; i<phi.size(); ++i)
            {
              // this is tricky.  ig is the _global_ dof index corresponding
              // to the _global_ vertex number elem->node(i).  Note that
              // in general these numbers will not be the same (except for
              // the case of one unknown per node on one subdomain) so
              // we need to go through the dof_map

              const Real f = q_point[gp]*q_point[gp];
              //    const Real f = (q_point[gp](0) +
              //    q_point[gp](1) +
              //    q_point[gp](2));

              // add jac*weight*f*phi to the RHS in position ig

              Fu(i) += JxW[gp]*f*phi[i][gp];
              Fv(i) += JxW[gp]*f*phi[i][gp];

              for (unsigned int j=0; j<phi.size(); ++j)
                {

                  Kuu(i,j) += JxW[gp]*((phi[i][gp])*(phi[j][gp]));

                  Kvv(i,j) += JxW[gp]*((phi[i][gp])*(phi[j][gp]) +
                                       1.*((dphi[i][gp])*(dphi[j][gp])));
                };
            };
          vol += JxW[gp];
        };


      // You can't compute "area" (perimeter) if you are in 2D
      if (dim == 3)
        {
          for (unsigned int side=0; side<elem->n_sides(); side++)
            if (elem->neighbor(side) == NULL)
              {
                fe_face->reinit (elem, side);

                //fe_face->print_info();

                for (unsigned int gp=0; gp<JxW_face.size(); gp++)
                  area += JxW_face[gp];
              }
        }

      // Constrain the DOF indices.
      dof_map.constrain_element_matrix_and_vector(Kuu, Fu, dof_indices_U);
      dof_map.constrain_element_matrix_and_vector(Kvv, Fv, dof_indices_V);


      system.rhs->add_vector(Fu, dof_indices_U);
      system.rhs->add_vector(Fv, dof_indices_V);

      system.matrix->add_matrix(Kuu, dof_indices_U);
      system.matrix->add_matrix(Kvv, dof_indices_V);
    }

  libMesh::out << "Vol="  << vol << std::endl;

  if (dim == 3)
    libMesh::out << "Area=" << area << std::endl;
}
Beispiel #27
0
// Define the matrix assembly function for the 1D PDE we are solving
void assemble_1D(EquationSystems& es, const std::string& system_name)
{

#ifdef LIBMESH_ENABLE_AMR

  // It is a good idea to check we are solving the correct system
  libmesh_assert_equal_to (system_name, "1D");

  // Get a reference to the mesh object
  const MeshBase& mesh = es.get_mesh();

  // The dimension we are using, i.e. dim==1
  const unsigned int dim = mesh.mesh_dimension();

  // Get a reference to the system we are solving
  LinearImplicitSystem& system = es.get_system<LinearImplicitSystem>("1D");

  // Get a reference to the DofMap object for this system. The DofMap object
  // handles the index translation from node and element numbers to degree of
  // freedom numbers. DofMap's are discussed in more detail in future examples.
  const DofMap& dof_map = system.get_dof_map();

  // Get a constant reference to the Finite Element type for the first
  // (and only) variable in the system.
  FEType fe_type = dof_map.variable_type(0);

  // Build a finite element object of the specified type. The build
  // function dynamically allocates memory so we use an UniquePtr in this case.
  // An UniquePtr is a pointer that cleans up after itself. See examples 3 and 4
  // for more details on UniquePtr.
  UniquePtr<FEBase> fe(FEBase::build(dim, fe_type));

  // Tell the finite element object to use fifth order Gaussian quadrature
  QGauss qrule(dim,FIFTH);
  fe->attach_quadrature_rule(&qrule);

  // Here we define some references to cell-specific data that will be used to
  // assemble the linear system.

  // The element Jacobian * quadrature weight at each integration point.
  const std::vector<Real>& JxW = fe->get_JxW();

  // The element shape functions evaluated at the quadrature points.
  const std::vector<std::vector<Real> >& phi = fe->get_phi();

  // The element shape function gradients evaluated at the quadrature points.
  const std::vector<std::vector<RealGradient> >& dphi = fe->get_dphi();

  // Declare a dense matrix and dense vector to hold the element matrix
  // and right-hand-side contribution
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  // This vector will hold the degree of freedom indices for the element.
  // These define where in the global system the element degrees of freedom
  // get mapped.
  std::vector<dof_id_type> dof_indices;

  // We now loop over all the active elements in the mesh in order to calculate
  // the matrix and right-hand-side contribution from each element. Use a
  // const_element_iterator to loop over the elements. We make
  // el_end const as it is used only for the stopping condition of the loop.
  MeshBase::const_element_iterator el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator el_end = mesh.active_local_elements_end();

  // Note that ++el is preferred to el++ when using loops with iterators
  for( ; el != el_end; ++el)
    {
      // It is convenient to store a pointer to the current element
      const Elem* elem = *el;

      // Get the degree of freedom indices for the current element.
      // These define where in the global matrix and right-hand-side this
      // element will contribute to.
      dof_map.dof_indices(elem, dof_indices);

      // Compute the element-specific data for the current element. This
      // involves computing the location of the quadrature points (q_point)
      // and the shape functions (phi, dphi) for the current element.
      fe->reinit(elem);

      // Store the number of local degrees of freedom contained in this element
      const int n_dofs = dof_indices.size();

      // We resize and zero out Ke and Fe (resize() also clears the matrix and
      // vector). In this example, all elements in the mesh are EDGE3's, so
      // Ke will always be 3x3, and Fe will always be 3x1. If the mesh contained
      // different element types, then the size of Ke and Fe would change.
      Ke.resize(n_dofs, n_dofs);
      Fe.resize(n_dofs);


      // Now loop over quadrature points to handle numerical integration
      for(unsigned int qp=0; qp<qrule.n_points(); qp++)
        {
          // Now build the element matrix and right-hand-side using loops to
          // integrate the test functions (i) against the trial functions (j).
          for(unsigned int i=0; i<phi.size(); i++)
            {
              Fe(i) += JxW[qp]*phi[i][qp];

              for(unsigned int j=0; j<phi.size(); j++)
                {
                  Ke(i,j) += JxW[qp]*(1.e-3*dphi[i][qp]*dphi[j][qp] +
                                      phi[i][qp]*phi[j][qp]);
                }
            }
        }


      // At this point we have completed the matrix and RHS summation. The
      // final step is to apply boundary conditions, which in this case are
      // simple Dirichlet conditions with u(0) = u(1) = 0.

      // Define the penalty parameter used to enforce the BC's
      double penalty = 1.e10;

      // Loop over the sides of this element. For a 1D element, the "sides"
      // are defined as the nodes on each edge of the element, i.e. 1D elements
      // have 2 sides.
      for(unsigned int s=0; s<elem->n_sides(); s++)
        {
          // If this element has a NULL neighbor, then it is on the edge of the
          // mesh and we need to enforce a boundary condition using the penalty
          // method.
          if(elem->neighbor(s) == NULL)
            {
              Ke(s,s) += penalty;
              Fe(s)   += 0*penalty;
            }
        }

      // This is a function call that is necessary when using adaptive
      // mesh refinement. See Adaptivity Example 2 for more details.
      dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices);

      // Add Ke and Fe to the global matrix and right-hand-side.
      system.matrix->add_matrix(Ke, dof_indices);
      system.rhs->add_vector(Fe, dof_indices);
    }
#endif // #ifdef LIBMESH_ENABLE_AMR
}
Beispiel #28
0
// We now define the matrix and rhs vector assembly function
// for the shell system.  This function implements the
// linear Kirchhoff-Love theory for thin shells.  At the
// end we also take into account the boundary conditions
// here, using the penalty method.
void assemble_shell (EquationSystems & es,
                     const std::string & libmesh_dbg_var(system_name))
{
  // It is a good idea to make sure we are assembling
  // the proper system.
  libmesh_assert_equal_to (system_name, "Shell");

  // Get a constant reference to the mesh object.
  const MeshBase & mesh = es.get_mesh();

  // Get a reference to the shell system object.
  LinearImplicitSystem & system = es.get_system<LinearImplicitSystem> ("Shell");

  // Get the shell parameters that we need during assembly.
  const Real h  = es.parameters.get<Real> ("thickness");
  const Real E  = es.parameters.get<Real> ("young's modulus");
  const Real nu = es.parameters.get<Real> ("poisson ratio");
  const Real q  = es.parameters.get<Real> ("uniform load");

  // Compute the membrane stiffness K and the bending
  // rigidity D from these parameters.
  const Real K = E * h     /     (1-nu*nu);
  const Real D = E * h*h*h / (12*(1-nu*nu));

  // Numeric ids corresponding to each variable in the system.
  const unsigned int u_var = system.variable_number ("u");
  const unsigned int v_var = system.variable_number ("v");
  const unsigned int w_var = system.variable_number ("w");

  // Get the Finite Element type for "u".  Note this will be
  // the same as the type for "v" and "w".
  FEType fe_type = system.variable_type (u_var);

  // Build a Finite Element object of the specified type.
  std::unique_ptr<FEBase> fe (FEBase::build(2, fe_type));

  // A Gauss quadrature rule for numerical integration.
  // For subdivision shell elements, a single Gauss point per
  // element is sufficient, hence we use extraorder = 0.
  const int extraorder = 0;
  std::unique_ptr<QBase> qrule (fe_type.default_quadrature_rule (2, extraorder));

  // Tell the finite element object to use our quadrature rule.
  fe->attach_quadrature_rule (qrule.get());

  // The element Jacobian * quadrature weight at each integration point.
  const std::vector<Real> & JxW = fe->get_JxW();

  // The surface tangents in both directions at the quadrature points.
  const std::vector<RealGradient> & dxyzdxi  = fe->get_dxyzdxi();
  const std::vector<RealGradient> & dxyzdeta = fe->get_dxyzdeta();

  // The second partial derivatives at the quadrature points.
  const std::vector<RealGradient> & d2xyzdxi2    = fe->get_d2xyzdxi2();
  const std::vector<RealGradient> & d2xyzdeta2   = fe->get_d2xyzdeta2();
  const std::vector<RealGradient> & d2xyzdxideta = fe->get_d2xyzdxideta();

  // The element shape function and its derivatives evaluated at the
  // quadrature points.
  const std::vector<std::vector<Real>> &          phi = fe->get_phi();
  const std::vector<std::vector<RealGradient>> & dphi = fe->get_dphi();
  const std::vector<std::vector<RealTensor>> &  d2phi = fe->get_d2phi();

  // A reference to the DofMap object for this system.  The DofMap
  // object handles the index translation from node and element numbers
  // to degree of freedom numbers.
  const DofMap & dof_map = system.get_dof_map();

  // Define data structures to contain the element stiffness matrix
  // and right-hand-side vector contribution.  Following
  // basic finite element terminology we will denote these
  // "Ke" and "Fe".
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  DenseSubMatrix<Number>
    Kuu(Ke), Kuv(Ke), Kuw(Ke),
    Kvu(Ke), Kvv(Ke), Kvw(Ke),
    Kwu(Ke), Kwv(Ke), Kww(Ke);

  DenseSubVector<Number>
    Fu(Fe),
    Fv(Fe),
    Fw(Fe);

  // This vector will hold the degree of freedom indices for
  // the element.  These define where in the global system
  // the element degrees of freedom get mapped.
  std::vector<dof_id_type> dof_indices;
  std::vector<dof_id_type> dof_indices_u;
  std::vector<dof_id_type> dof_indices_v;
  std::vector<dof_id_type> dof_indices_w;

  // Now we will loop over all the elements in the mesh.  We will
  // compute the element matrix and right-hand-side contribution.
  for (const auto & elem : mesh.active_local_element_ptr_range())
    {
      // The ghost elements at the boundaries need to be excluded
      // here, as they don't belong to the physical shell,
      // but serve for a proper boundary treatment only.
      libmesh_assert_equal_to (elem->type(), TRI3SUBDIVISION);
      const Tri3Subdivision * sd_elem = static_cast<const Tri3Subdivision *> (elem);
      if (sd_elem->is_ghost())
        continue;

      // Get the degree of freedom indices for the
      // current element.  These define where in the global
      // matrix and right-hand-side this element will
      // contribute to.
      dof_map.dof_indices (elem, dof_indices);
      dof_map.dof_indices (elem, dof_indices_u, u_var);
      dof_map.dof_indices (elem, dof_indices_v, v_var);
      dof_map.dof_indices (elem, dof_indices_w, w_var);

      const std::size_t n_dofs   = dof_indices.size();
      const std::size_t n_u_dofs = dof_indices_u.size();
      const std::size_t n_v_dofs = dof_indices_v.size();
      const std::size_t n_w_dofs = dof_indices_w.size();

      // Compute the element-specific data for the current
      // element.  This involves computing the location of the
      // quadrature points and the shape functions
      // (phi, dphi, d2phi) for the current element.
      fe->reinit (elem);

      // Zero the element matrix and right-hand side before
      // summing them.  We use the resize member here because
      // the number of degrees of freedom might have changed from
      // the last element.
      Ke.resize (n_dofs, n_dofs);
      Fe.resize (n_dofs);

      // Reposition the submatrices...  The idea is this:
      //
      //         -           -          -  -
      //        | Kuu Kuv Kuw |        | Fu |
      //   Ke = | Kvu Kvv Kvw |;  Fe = | Fv |
      //        | Kwu Kwv Kww |        | Fw |
      //         -           -          -  -
      //
      // The DenseSubMatrix.reposition () member takes the
      // (row_offset, column_offset, row_size, column_size).
      //
      // Similarly, the DenseSubVector.reposition () member
      // takes the (row_offset, row_size)
      Kuu.reposition (u_var*n_u_dofs, u_var*n_u_dofs, n_u_dofs, n_u_dofs);
      Kuv.reposition (u_var*n_u_dofs, v_var*n_u_dofs, n_u_dofs, n_v_dofs);
      Kuw.reposition (u_var*n_u_dofs, w_var*n_u_dofs, n_u_dofs, n_w_dofs);

      Kvu.reposition (v_var*n_v_dofs, u_var*n_v_dofs, n_v_dofs, n_u_dofs);
      Kvv.reposition (v_var*n_v_dofs, v_var*n_v_dofs, n_v_dofs, n_v_dofs);
      Kvw.reposition (v_var*n_v_dofs, w_var*n_v_dofs, n_v_dofs, n_w_dofs);

      Kwu.reposition (w_var*n_w_dofs, u_var*n_w_dofs, n_w_dofs, n_u_dofs);
      Kwv.reposition (w_var*n_w_dofs, v_var*n_w_dofs, n_w_dofs, n_v_dofs);
      Kww.reposition (w_var*n_w_dofs, w_var*n_w_dofs, n_w_dofs, n_w_dofs);

      Fu.reposition (u_var*n_u_dofs, n_u_dofs);
      Fv.reposition (v_var*n_u_dofs, n_v_dofs);
      Fw.reposition (w_var*n_u_dofs, n_w_dofs);

      // Now we will build the element matrix and right-hand-side.
      for (unsigned int qp=0; qp<qrule->n_points(); ++qp)
        {
          // First, we compute the external force resulting
          // from a load q distributed uniformly across the plate.
          // Since the load is supposed to be transverse to the plate,
          // it affects the z-direction, i.e. the "w" variable.
          for (unsigned int i=0; i<n_u_dofs; ++i)
            Fw(i) += JxW[qp] * phi[i][qp] * q;

          // Next, we assemble the stiffness matrix.  This is only valid
          // for the linear theory, i.e., for small deformations, where
          // reference and deformed surface metrics are indistinguishable.

          // Get the three surface basis vectors.
          const RealVectorValue & a1 = dxyzdxi[qp];
          const RealVectorValue & a2 = dxyzdeta[qp];
          RealVectorValue   a3 = a1.cross(a2);
          const Real jac = a3.norm(); // the surface Jacobian
          libmesh_assert_greater (jac, 0);
          a3 /= jac; // the shell director a3 is normalized to unit length

          // Get the derivatives of the surface tangents.
          const RealVectorValue & a11 = d2xyzdxi2[qp];
          const RealVectorValue & a22 = d2xyzdeta2[qp];
          const RealVectorValue & a12 = d2xyzdxideta[qp];

          // Compute the three covariant components of the first
          // fundamental form of the surface.
          const RealVectorValue a(a1*a1, a2*a2, a1*a2);

          // The elastic H matrix in Voigt's notation, computed from the
          // covariant components of the first fundamental form rather
          // than the contravariant components, exploiting that the
          // contravariant first fundamental form is the inverse of the
          // covariant first fundamental form (hence the determinant etc.).
          RealTensorValue H;
          H(0,0) = a(1) * a(1);
          H(0,1) = H(1,0) = nu * a(1) * a(0) + (1-nu) * a(2) * a(2);
          H(0,2) = H(2,0) = -a(1) * a(2);
          H(1,1) = a(0) * a(0);
          H(1,2) = H(2,1) = -a(0) * a(2);
          H(2,2) = 0.5 * ((1-nu) * a(1) * a(0) + (1+nu) * a(2) * a(2));
          const Real det = a(0) * a(1) - a(2) * a(2);
          libmesh_assert_not_equal_to (det * det, 0);
          H /= det * det;

          // Precompute come cross products for the bending part below.
          const RealVectorValue a11xa2 = a11.cross(a2);
          const RealVectorValue a22xa2 = a22.cross(a2);
          const RealVectorValue a12xa2 = a12.cross(a2);
          const RealVectorValue a1xa11 =  a1.cross(a11);
          const RealVectorValue a1xa22 =  a1.cross(a22);
          const RealVectorValue a1xa12 =  a1.cross(a12);
          const RealVectorValue a2xa3  =  a2.cross(a3);
          const RealVectorValue a3xa1  =  a3.cross(a1);

          // Loop over all pairs of nodes I,J.
          for (unsigned int i=0; i<n_u_dofs; ++i)
            {
              for (unsigned int j=0; j<n_u_dofs; ++j)
                {
                  // The membrane strain matrices in Voigt's notation.
                  RealTensorValue MI, MJ;
                  for (unsigned int k=0; k<3; ++k)
                    {
                      MI(0,k) = dphi[i][qp](0) * a1(k);
                      MI(1,k) = dphi[i][qp](1) * a2(k);
                      MI(2,k) = dphi[i][qp](1) * a1(k)
                        + dphi[i][qp](0) * a2(k);

                      MJ(0,k) = dphi[j][qp](0) * a1(k);
                      MJ(1,k) = dphi[j][qp](1) * a2(k);
                      MJ(2,k) = dphi[j][qp](1) * a1(k)
                        + dphi[j][qp](0) * a2(k);
                    }

                  // The bending strain matrices in Voigt's notation.
                  RealTensorValue BI, BJ;
                  for (unsigned int k=0; k<3; ++k)
                    {
                      const Real term_ik = dphi[i][qp](0) * a2xa3(k)
                        + dphi[i][qp](1) * a3xa1(k);
                      BI(0,k) = -d2phi[i][qp](0,0) * a3(k)
                        +(dphi[i][qp](0) * a11xa2(k)
                          + dphi[i][qp](1) * a1xa11(k)
                          + (a3*a11) * term_ik) / jac;
                      BI(1,k) = -d2phi[i][qp](1,1) * a3(k)
                        +(dphi[i][qp](0) * a22xa2(k)
                          + dphi[i][qp](1) * a1xa22(k)
                          + (a3*a22) * term_ik) / jac;
                      BI(2,k) = 2 * (-d2phi[i][qp](0,1) * a3(k)
                                     +(dphi[i][qp](0) * a12xa2(k)
                                       + dphi[i][qp](1) * a1xa12(k)
                                       + (a3*a12) * term_ik) / jac);

                      const Real term_jk = dphi[j][qp](0) * a2xa3(k)
                        + dphi[j][qp](1) * a3xa1(k);
                      BJ(0,k) = -d2phi[j][qp](0,0) * a3(k)
                        +(dphi[j][qp](0) * a11xa2(k)
                          + dphi[j][qp](1) * a1xa11(k)
                          + (a3*a11) * term_jk) / jac;
                      BJ(1,k) = -d2phi[j][qp](1,1) * a3(k)
                        +(dphi[j][qp](0) * a22xa2(k)
                          + dphi[j][qp](1) * a1xa22(k)
                          + (a3*a22) * term_jk) / jac;
                      BJ(2,k) = 2 * (-d2phi[j][qp](0,1) * a3(k)
                                     +(dphi[j][qp](0) * a12xa2(k)
                                       + dphi[j][qp](1) * a1xa12(k)
                                       + (a3*a12) * term_jk) / jac);
                    }

                  // The total stiffness matrix coupling the nodes
                  // I and J is a sum of membrane and bending
                  // contributions according to the following formula.
                  const RealTensorValue KIJ = JxW[qp] * K * MI.transpose() * H * MJ
                    + JxW[qp] * D * BI.transpose() * H * BJ;

                  // Insert the components of the coupling stiffness
                  // matrix KIJ into the corresponding directional
                  // submatrices.
                  Kuu(i,j) += KIJ(0,0);
                  Kuv(i,j) += KIJ(0,1);
                  Kuw(i,j) += KIJ(0,2);

                  Kvu(i,j) += KIJ(1,0);
                  Kvv(i,j) += KIJ(1,1);
                  Kvw(i,j) += KIJ(1,2);

                  Kwu(i,j) += KIJ(2,0);
                  Kwv(i,j) += KIJ(2,1);
                  Kww(i,j) += KIJ(2,2);
                }
            }

        } // end of the quadrature point qp-loop

      // The element matrix and right-hand-side are now built
      // for this element.  Add them to the global matrix and
      // right-hand-side vector.  The NumericMatrix::add_matrix()
      // and NumericVector::add_vector() members do this for us.
      system.matrix->add_matrix (Ke, dof_indices);
      system.rhs->add_vector    (Fe, dof_indices);
    } // end of non-ghost element loop

  // Next, we apply the boundary conditions.  In this case,
  // all boundaries are clamped by the penalty method, using
  // the special "ghost" nodes along the boundaries.  Note
  // that there are better ways to implement boundary conditions
  // for subdivision shells.  We use the simplest way here,
  // which is known to be overly restrictive and will lead to
  // a slightly too small deformation of the plate.
  for (const auto & elem : mesh.active_local_element_ptr_range())
    {
      // For the boundary conditions, we only need to loop over
      // the ghost elements.
      libmesh_assert_equal_to (elem->type(), TRI3SUBDIVISION);
      const Tri3Subdivision * gh_elem = static_cast<const Tri3Subdivision *> (elem);
      if (!gh_elem->is_ghost())
        continue;

      // Find the side which is part of the physical plate boundary,
      // that is, the boundary of the original mesh without ghosts.
      for (auto s : elem->side_index_range())
        {
          const Tri3Subdivision * nb_elem = static_cast<const Tri3Subdivision *> (elem->neighbor_ptr(s));
          if (nb_elem == libmesh_nullptr || nb_elem->is_ghost())
            continue;

          /*
           * Determine the four nodes involved in the boundary
           * condition treatment of this side.  The MeshTools::Subdiv
           * namespace provides lookup tables next and prev
           * for an efficient determination of the next and previous
           * nodes of an element, respectively.
           *
           *      n4
           *     /  \
           *    / gh \
           *  n2 ---- n3
           *    \ nb /
           *     \  /
           *      n1
           */
          const Node * nodes [4]; // n1, n2, n3, n4
          nodes[1] = gh_elem->node_ptr(s); // n2
          nodes[2] = gh_elem->node_ptr(MeshTools::Subdivision::next[s]); // n3
          nodes[3] = gh_elem->node_ptr(MeshTools::Subdivision::prev[s]); // n4

          // The node in the interior of the domain, n1, is the
          // hardest to find.  Walk along the edges of element nb until
          // we have identified it.
          unsigned int n_int = 0;
          nodes[0] = nb_elem->node_ptr(0);
          while (nodes[0]->id() == nodes[1]->id() || nodes[0]->id() == nodes[2]->id())
            nodes[0] = nb_elem->node_ptr(++n_int);

          // The penalty value.  \f$ \frac{1}{\epsilon} \f$
          const Real penalty = 1.e10;

          // With this simple method, clamped boundary conditions are
          // obtained by penalizing the displacements of all four nodes.
          // This ensures that the displacement field vanishes on the
          // boundary side s.
          for (unsigned int n=0; n<4; ++n)
            {
              const dof_id_type u_dof = nodes[n]->dof_number (system.number(), u_var, 0);
              const dof_id_type v_dof = nodes[n]->dof_number (system.number(), v_var, 0);
              const dof_id_type w_dof = nodes[n]->dof_number (system.number(), w_var, 0);
              system.matrix->add (u_dof, u_dof, penalty);
              system.matrix->add (v_dof, v_dof, penalty);
              system.matrix->add (w_dof, w_dof, penalty);
            }
        }
    } // end of ghost element loop
}
// The matrix assembly function to be called at each time step to
// prepare for the linear solve.
void assemble_stokes (EquationSystems & es,
                      const std::string & system_name)
{
  // It is a good idea to make sure we are assembling
  // the proper system.
  libmesh_assert_equal_to (system_name, "Navier-Stokes");

  // Get a constant reference to the mesh object.
  const MeshBase & mesh = es.get_mesh();

  // The dimension that we are running
  const unsigned int dim = mesh.mesh_dimension();

  // Get a reference to the Stokes system object.
  TransientLinearImplicitSystem & navier_stokes_system =
    es.get_system<TransientLinearImplicitSystem> ("Navier-Stokes");

  // Numeric ids corresponding to each variable in the system
  const unsigned int u_var = navier_stokes_system.variable_number ("u");
  const unsigned int v_var = navier_stokes_system.variable_number ("v");
  const unsigned int p_var = navier_stokes_system.variable_number ("p");
  const unsigned int alpha_var = navier_stokes_system.variable_number ("alpha");

  // Get the Finite Element type for "u".  Note this will be
  // the same as the type for "v".
  FEType fe_vel_type = navier_stokes_system.variable_type(u_var);

  // Get the Finite Element type for "p".
  FEType fe_pres_type = navier_stokes_system.variable_type(p_var);

  // Build a Finite Element object of the specified type for
  // the velocity variables.
  UniquePtr<FEBase> fe_vel  (FEBase::build(dim, fe_vel_type));

  // Build a Finite Element object of the specified type for
  // the pressure variables.
  UniquePtr<FEBase> fe_pres (FEBase::build(dim, fe_pres_type));

  // A Gauss quadrature rule for numerical integration.
  // Let the FEType object decide what order rule is appropriate.
  QGauss qrule (dim, fe_vel_type.default_quadrature_order());

  // Tell the finite element objects to use our quadrature rule.
  fe_vel->attach_quadrature_rule (&qrule);
  fe_pres->attach_quadrature_rule (&qrule);

  // Here we define some references to cell-specific data that
  // will be used to assemble the linear system.
  //
  // The element Jacobian * quadrature weight at each integration point.
  const std::vector<Real> & JxW = fe_vel->get_JxW();

  // The element shape functions evaluated at the quadrature points.
  const std::vector<std::vector<Real> > & phi = fe_vel->get_phi();

  // The element shape function gradients for the velocity
  // variables evaluated at the quadrature points.
  const std::vector<std::vector<RealGradient> > & dphi = fe_vel->get_dphi();

  // The element shape functions for the pressure variable
  // evaluated at the quadrature points.
  const std::vector<std::vector<Real> > & psi = fe_pres->get_phi();

  // The value of the linear shape function gradients at the quadrature points
  // const std::vector<std::vector<RealGradient> > & dpsi = fe_pres->get_dphi();

  // A reference to the DofMap object for this system.  The DofMap
  // object handles the index translation from node and element numbers
  // to degree of freedom numbers.  We will talk more about the DofMap
  // in future examples.
  const DofMap & dof_map = navier_stokes_system.get_dof_map();

  // Define data structures to contain the element matrix
  // and right-hand-side vector contribution.  Following
  // basic finite element terminology we will denote these
  // "Ke" and "Fe".
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  DenseSubMatrix<Number>
    Kuu(Ke), Kuv(Ke), Kup(Ke),
    Kvu(Ke), Kvv(Ke), Kvp(Ke),
    Kpu(Ke), Kpv(Ke), Kpp(Ke);
  DenseSubMatrix<Number> Kalpha_p(Ke), Kp_alpha(Ke);

  DenseSubVector<Number>
    Fu(Fe),
    Fv(Fe),
    Fp(Fe);

  // This vector will hold the degree of freedom indices for
  // the element.  These define where in the global system
  // the element degrees of freedom get mapped.
  std::vector<dof_id_type> dof_indices;
  std::vector<dof_id_type> dof_indices_u;
  std::vector<dof_id_type> dof_indices_v;
  std::vector<dof_id_type> dof_indices_p;
  std::vector<dof_id_type> dof_indices_alpha;

  // Find out what the timestep size parameter is from the system, and
  // the value of theta for the theta method.  We use implicit Euler (theta=1)
  // for this simulation even though it is only first-order accurate in time.
  // The reason for this decision is that the second-order Crank-Nicolson
  // method is notoriously oscillatory for problems with discontinuous
  // initial data such as the lid-driven cavity.  Therefore,
  // we sacrifice accuracy in time for stability, but since the solution
  // reaches steady state relatively quickly we can afford to take small
  // timesteps.  If you monitor the initial nonlinear residual for this
  // simulation, you should see that it is monotonically decreasing in time.
  const Real dt    = es.parameters.get<Real>("dt");
  // const Real time  = es.parameters.get<Real>("time");
  const Real theta = 1.;

  // Now we will loop over all the elements in the mesh that
  // live on the local processor. We will compute the element
  // matrix and right-hand-side contribution.  Since the mesh
  // will be refined we want to only consider the ACTIVE elements,
  // hence we use a variant of the active_elem_iterator.
  MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
  const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end();

  for ( ; el != end_el; ++el)
    {
      // Store a pointer to the element we are currently
      // working on.  This allows for nicer syntax later.
      const Elem * elem = *el;

      // Get the degree of freedom indices for the
      // current element.  These define where in the global
      // matrix and right-hand-side this element will
      // contribute to.
      dof_map.dof_indices (elem, dof_indices);
      dof_map.dof_indices (elem, dof_indices_u, u_var);
      dof_map.dof_indices (elem, dof_indices_v, v_var);
      dof_map.dof_indices (elem, dof_indices_p, p_var);
      dof_map.dof_indices (elem, dof_indices_alpha, alpha_var);

      const unsigned int n_dofs   = dof_indices.size();
      const unsigned int n_u_dofs = dof_indices_u.size();
      const unsigned int n_v_dofs = dof_indices_v.size();
      const unsigned int n_p_dofs = dof_indices_p.size();

      // Compute the element-specific data for the current
      // element.  This involves computing the location of the
      // quadrature points (q_point) and the shape functions
      // (phi, dphi) for the current element.
      fe_vel->reinit  (elem);
      fe_pres->reinit (elem);

      // Zero the element matrix and right-hand side before
      // summing them.  We use the resize member here because
      // the number of degrees of freedom might have changed from
      // the last element.  Note that this will be the case if the
      // element type is different (i.e. the last element was a
      // triangle, now we are on a quadrilateral).
      Ke.resize (n_dofs, n_dofs);
      Fe.resize (n_dofs);

      // Reposition the submatrices...  The idea is this:
      //
      //         -           -          -  -
      //        | Kuu Kuv Kup |        | Fu |
      //   Ke = | Kvu Kvv Kvp |;  Fe = | Fv |
      //        | Kpu Kpv Kpp |        | Fp |
      //         -           -          -  -
      //
      // The DenseSubMatrix.repostition () member takes the
      // (row_offset, column_offset, row_size, column_size).
      //
      // Similarly, the DenseSubVector.reposition () member
      // takes the (row_offset, row_size)
      Kuu.reposition (u_var*n_u_dofs, u_var*n_u_dofs, n_u_dofs, n_u_dofs);
      Kuv.reposition (u_var*n_u_dofs, v_var*n_u_dofs, n_u_dofs, n_v_dofs);
      Kup.reposition (u_var*n_u_dofs, p_var*n_u_dofs, n_u_dofs, n_p_dofs);

      Kvu.reposition (v_var*n_v_dofs, u_var*n_v_dofs, n_v_dofs, n_u_dofs);
      Kvv.reposition (v_var*n_v_dofs, v_var*n_v_dofs, n_v_dofs, n_v_dofs);
      Kvp.reposition (v_var*n_v_dofs, p_var*n_v_dofs, n_v_dofs, n_p_dofs);

      Kpu.reposition (p_var*n_u_dofs, u_var*n_u_dofs, n_p_dofs, n_u_dofs);
      Kpv.reposition (p_var*n_u_dofs, v_var*n_u_dofs, n_p_dofs, n_v_dofs);
      Kpp.reposition (p_var*n_u_dofs, p_var*n_u_dofs, n_p_dofs, n_p_dofs);

      // Also, add a row and a column to constrain the pressure
      Kp_alpha.reposition (p_var*n_u_dofs, p_var*n_u_dofs+n_p_dofs, n_p_dofs, 1);
      Kalpha_p.reposition (p_var*n_u_dofs+n_p_dofs, p_var*n_u_dofs, 1, n_p_dofs);


      Fu.reposition (u_var*n_u_dofs, n_u_dofs);
      Fv.reposition (v_var*n_u_dofs, n_v_dofs);
      Fp.reposition (p_var*n_u_dofs, n_p_dofs);

      // Now we will build the element matrix and right-hand-side.
      // Constructing the RHS requires the solution and its
      // gradient from the previous timestep.  This must be
      // calculated at each quadrature point by summing the
      // solution degree-of-freedom values by the appropriate
      // weight functions.
      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        {
          // Values to hold the solution & its gradient at the previous timestep.
          Number u = 0., u_old = 0.;
          Number v = 0., v_old = 0.;
          Number p_old = 0.;
          Gradient grad_u, grad_u_old;
          Gradient grad_v, grad_v_old;

          // Compute the velocity & its gradient from the previous timestep
          // and the old Newton iterate.
          for (unsigned int l=0; l<n_u_dofs; l++)
            {
              // From the old timestep:
              u_old += phi[l][qp]*navier_stokes_system.old_solution (dof_indices_u[l]);
              v_old += phi[l][qp]*navier_stokes_system.old_solution (dof_indices_v[l]);
              grad_u_old.add_scaled (dphi[l][qp], navier_stokes_system.old_solution (dof_indices_u[l]));
              grad_v_old.add_scaled (dphi[l][qp], navier_stokes_system.old_solution (dof_indices_v[l]));

              // From the previous Newton iterate:
              u += phi[l][qp]*navier_stokes_system.current_solution (dof_indices_u[l]);
              v += phi[l][qp]*navier_stokes_system.current_solution (dof_indices_v[l]);
              grad_u.add_scaled (dphi[l][qp], navier_stokes_system.current_solution (dof_indices_u[l]));
              grad_v.add_scaled (dphi[l][qp], navier_stokes_system.current_solution (dof_indices_v[l]));
            }

          // Compute the old pressure value at this quadrature point.
          for (unsigned int l=0; l<n_p_dofs; l++)
            p_old += psi[l][qp]*navier_stokes_system.old_solution (dof_indices_p[l]);

          // Definitions for convenience.  It is sometimes simpler to do a
          // dot product if you have the full vector at your disposal.
          const NumberVectorValue U_old (u_old, v_old);
          const NumberVectorValue U     (u,     v);
          const Number u_x = grad_u(0);
          const Number u_y = grad_u(1);
          const Number v_x = grad_v(0);
          const Number v_y = grad_v(1);

          // First, an i-loop over the velocity degrees of freedom.
          // We know that n_u_dofs == n_v_dofs so we can compute contributions
          // for both at the same time.
          for (unsigned int i=0; i<n_u_dofs; i++)
            {
              Fu(i) += JxW[qp]*(u_old*phi[i][qp] -                            // mass-matrix term
                                (1.-theta)*dt*(U_old*grad_u_old)*phi[i][qp] + // convection term
                                (1.-theta)*dt*p_old*dphi[i][qp](0)  -         // pressure term on rhs
                                (1.-theta)*dt*(grad_u_old*dphi[i][qp]) +      // diffusion term on rhs
                                theta*dt*(U*grad_u)*phi[i][qp]);              // Newton term


              Fv(i) += JxW[qp]*(v_old*phi[i][qp] -                             // mass-matrix term
                                (1.-theta)*dt*(U_old*grad_v_old)*phi[i][qp] +  // convection term
                                (1.-theta)*dt*p_old*dphi[i][qp](1) -           // pressure term on rhs
                                (1.-theta)*dt*(grad_v_old*dphi[i][qp]) +       // diffusion term on rhs
                                theta*dt*(U*grad_v)*phi[i][qp]);               // Newton term


              // Note that the Fp block is identically zero unless we are using
              // some kind of artificial compressibility scheme...

              // Matrix contributions for the uu and vv couplings.
              for (unsigned int j=0; j<n_u_dofs; j++)
                {
                  Kuu(i,j) += JxW[qp]*(phi[i][qp]*phi[j][qp] +                // mass matrix term
                                       theta*dt*(dphi[i][qp]*dphi[j][qp]) +   // diffusion term
                                       theta*dt*(U*dphi[j][qp])*phi[i][qp] +  // convection term
                                       theta*dt*u_x*phi[i][qp]*phi[j][qp]);   // Newton term

                  Kuv(i,j) += JxW[qp]*theta*dt*u_y*phi[i][qp]*phi[j][qp];     // Newton term

                  Kvv(i,j) += JxW[qp]*(phi[i][qp]*phi[j][qp] +                // mass matrix term
                                       theta*dt*(dphi[i][qp]*dphi[j][qp]) +   // diffusion term
                                       theta*dt*(U*dphi[j][qp])*phi[i][qp] +  // convection term
                                       theta*dt*v_y*phi[i][qp]*phi[j][qp]);   // Newton term

                  Kvu(i,j) += JxW[qp]*theta*dt*v_x*phi[i][qp]*phi[j][qp];     // Newton term
                }

              // Matrix contributions for the up and vp couplings.
              for (unsigned int j=0; j<n_p_dofs; j++)
                {
                  Kup(i,j) += JxW[qp]*(-theta*dt*psi[j][qp]*dphi[i][qp](0));
                  Kvp(i,j) += JxW[qp]*(-theta*dt*psi[j][qp]*dphi[i][qp](1));
                }
            }

          // Now an i-loop over the pressure degrees of freedom.  This code computes
          // the matrix entries due to the continuity equation.  Note: To maintain a
          // symmetric matrix, we may (or may not) multiply the continuity equation by
          // negative one.  Here we do not.
          for (unsigned int i=0; i<n_p_dofs; i++)
            {
              Kp_alpha(i,0) += JxW[qp]*psi[i][qp];
              Kalpha_p(0,i) += JxW[qp]*psi[i][qp];
              for (unsigned int j=0; j<n_u_dofs; j++)
                {
                  Kpu(i,j) += JxW[qp]*psi[i][qp]*dphi[j][qp](0);
                  Kpv(i,j) += JxW[qp]*psi[i][qp]*dphi[j][qp](1);
                }
            }
        } // end of the quadrature point qp-loop


      // At this point the interior element integration has
      // been completed.  However, we have not yet addressed
      // boundary conditions.  For this example we will only
      // consider simple Dirichlet boundary conditions imposed
      // via the penalty method. The penalty method used here
      // is equivalent (for Lagrange basis functions) to lumping
      // the matrix resulting from the L2 projection penalty
      // approach introduced in example 3.
      {
        // The penalty value.  \f$ \frac{1}{\epsilon} \f$
        const Real penalty = 1.e10;

        // The following loops over the sides of the element.
        // If the element has no neighbor on a side then that
        // side MUST live on a boundary of the domain.
        for (unsigned int s=0; s<elem->n_sides(); s++)
          if (elem->neighbor(s) == libmesh_nullptr)
            {
              UniquePtr<Elem> side (elem->build_side(s));

              // Loop over the nodes on the side.
              for (unsigned int ns=0; ns<side->n_nodes(); ns++)
                {
                  // Boundary ids are set internally by
                  // build_square().
                  // 0=bottom
                  // 1=right
                  // 2=top
                  // 3=left

                  // Set u = 1 on the top boundary, 0 everywhere else
                  const Real u_value =
                    (mesh.get_boundary_info().has_boundary_id(elem, s, 2))
                    ? 1. : 0.;

                  // Set v = 0 everywhere
                  const Real v_value = 0.;

                  // Find the node on the element matching this node on
                  // the side.  That defined where in the element matrix
                  // the boundary condition will be applied.
                  for (unsigned int n=0; n<elem->n_nodes(); n++)
                    if (elem->node_id(n) == side->node_id(ns))
                      {
                        // Matrix contribution.
                        Kuu(n,n) += penalty;
                        Kvv(n,n) += penalty;

                        // Right-hand-side contribution.
                        Fu(n) += penalty*u_value;
                        Fv(n) += penalty*v_value;
                      }
                } // end face node loop
            } // end if (elem->neighbor(side) == libmesh_nullptr)
      } // end boundary condition section

      // If this assembly program were to be used on an adaptive mesh,
      // we would have to apply any hanging node constraint equations
      dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices);

      // The element matrix and right-hand-side are now built
      // for this element.  Add them to the global matrix and
      // right-hand-side vector.  The SparseMatrix::add_matrix()
      // and NumericVector::add_vector() members do this for us.
      navier_stokes_system.matrix->add_matrix (Ke, dof_indices);
      navier_stokes_system.rhs->add_vector    (Fe, dof_indices);
    } // end of element loop

  // We can set the mean of the pressure by setting Falpha
  navier_stokes_system.rhs->add(navier_stokes_system.rhs->size()-1, 10.);
}
Beispiel #30
0
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;
}