Exemple #1
0
void FEMap::compute_face_map(int dim, const std::vector<Real>& qw,
                             const Elem* side)
{
  libmesh_assert(side);

  START_LOG("compute_face_map()", "FEMap");

  // The number of quadrature points.
  const unsigned int n_qp = cast_int<unsigned int>(qw.size());

  switch (dim)
    {
    case 1:
      {
        // A 1D finite element, currently assumed to be in 1D space
        // This means the boundary is a "0D finite element", a
        // NODEELEM.

        // Resize the vectors to hold data at the quadrature points
        {
          this->xyz.resize(n_qp);
          normals.resize(n_qp);

          this->JxW.resize(n_qp);
        }

        // If we have no quadrature points, there's nothing else to do
        if (!n_qp)
          break;

        // We need to look back at the full edge to figure out the normal
        // vector
        const Elem *elem = side->parent();
        libmesh_assert (elem);
        if (side->node(0) == elem->node(0))
          normals[0] = Point(-1.);
        else
          {
            libmesh_assert_equal_to (side->node(0), elem->node(1));
            normals[0] = Point(1.);
          }

        // Calculate x at the point
        libmesh_assert_equal_to (this->psi_map.size(), 1);
        // In the unlikely event we have multiple quadrature
        // points, they'll be in the same place
        for (unsigned int p=0; p<n_qp; p++)
          {
            this->xyz[p].zero();
            this->xyz[p].add_scaled          (side->point(0), this->psi_map[0][p]);
            normals[p] = normals[0];
            this->JxW[p] = 1.0*qw[p];
          }

        // done computing the map
        break;
      }

    case 2:
      {
        // A 2D finite element living in either 2D or 3D space.
        // This means the boundary is a 1D finite element, i.e.
        // and EDGE2 or EDGE3.
        // Resize the vectors to hold data at the quadrature points
        {
          this->xyz.resize(n_qp);
          this->dxyzdxi_map.resize(n_qp);
          this->d2xyzdxi2_map.resize(n_qp);
          this->tangents.resize(n_qp);
          this->normals.resize(n_qp);
          this->curvatures.resize(n_qp);

          this->JxW.resize(n_qp);
        }

        // Clear the entities that will be summed
        // Compute the tangent & normal at the quadrature point
        for (unsigned int p=0; p<n_qp; p++)
          {
            this->tangents[p].resize(LIBMESH_DIM-1); // 1 Tangent in 2D, 2 in 3D
            this->xyz[p].zero();
            this->dxyzdxi_map[p].zero();
            this->d2xyzdxi2_map[p].zero();
          }

        // compute x, dxdxi at the quadrature points
        for (unsigned int i=0; i<this->psi_map.size(); i++) // sum over the nodes
          {
            const Point& side_point = side->point(i);

            for (unsigned int p=0; p<n_qp; p++) // for each quadrature point...
              {
                this->xyz[p].add_scaled          (side_point, this->psi_map[i][p]);
                this->dxyzdxi_map[p].add_scaled  (side_point, this->dpsidxi_map[i][p]);
                this->d2xyzdxi2_map[p].add_scaled(side_point, this->d2psidxi2_map[i][p]);
              }
          }

        // Compute the tangent & normal at the quadrature point
        for (unsigned int p=0; p<n_qp; p++)
          {
            // The first tangent comes from just the edge's Jacobian
            this->tangents[p][0] = this->dxyzdxi_map[p].unit();

#if LIBMESH_DIM == 2
            // For a 2D element living in 2D, the normal is given directly
            // from the entries in the edge Jacobian.
            this->normals[p] = (Point(this->dxyzdxi_map[p](1), -this->dxyzdxi_map[p](0), 0.)).unit();

#elif LIBMESH_DIM == 3
            // For a 2D element living in 3D, there is a second tangent.
            // For the second tangent, we need to refer to the full
            // element's (not just the edge's) Jacobian.
            const Elem *elem = side->parent();
            libmesh_assert(elem);

            // Inverse map xyz[p] to a reference point on the parent...
            Point reference_point = FE<2,LAGRANGE>::inverse_map(elem, this->xyz[p]);

            // Get dxyz/dxi and dxyz/deta from the parent map.
            Point dx_dxi  = FE<2,LAGRANGE>::map_xi (elem, reference_point);
            Point dx_deta = FE<2,LAGRANGE>::map_eta(elem, reference_point);

            // The second tangent vector is formed by crossing these vectors.
            tangents[p][1] = dx_dxi.cross(dx_deta).unit();

            // Finally, the normal in this case is given by crossing these
            // two tangents.
            normals[p] = tangents[p][0].cross(tangents[p][1]).unit();
#endif


            // The curvature is computed via the familiar Frenet formula:
            // curvature = [d^2(x) / d (xi)^2] dot [normal]
            // For a reference, see:
            // F.S. Merritt, Mathematics Manual, 1962, McGraw-Hill, p. 310
            //
            // Note: The sign convention here is different from the
            // 3D case.  Concave-upward curves (smiles) have a positive
            // curvature.  Concave-downward curves (frowns) have a
            // negative curvature.  Be sure to take that into account!
            const Real numerator   = this->d2xyzdxi2_map[p] * this->normals[p];
            const Real denominator = this->dxyzdxi_map[p].size_sq();
            libmesh_assert_not_equal_to (denominator, 0);
            curvatures[p] = numerator / denominator;
          }

        // compute the jacobian at the quadrature points
        for (unsigned int p=0; p<n_qp; p++)
          {
            const Real the_jac = this->dxyzdxi_map[p].size();

            libmesh_assert_greater (the_jac, 0.);

            this->JxW[p] = the_jac*qw[p];
          }

        // done computing the map
        break;
      }



    case 3:
      {
        // A 3D finite element living in 3D space.
        // Resize the vectors to hold data at the quadrature points
        {
          this->xyz.resize(n_qp);
          this->dxyzdxi_map.resize(n_qp);
          this->dxyzdeta_map.resize(n_qp);
          this->d2xyzdxi2_map.resize(n_qp);
          this->d2xyzdxideta_map.resize(n_qp);
          this->d2xyzdeta2_map.resize(n_qp);
          this->tangents.resize(n_qp);
          this->normals.resize(n_qp);
          this->curvatures.resize(n_qp);

          this->JxW.resize(n_qp);
        }

        // Clear the entities that will be summed
        for (unsigned int p=0; p<n_qp; p++)
          {
            this->tangents[p].resize(LIBMESH_DIM-1); // 1 Tangent in 2D, 2 in 3D
            this->xyz[p].zero();
            this->dxyzdxi_map[p].zero();
            this->dxyzdeta_map[p].zero();
            this->d2xyzdxi2_map[p].zero();
            this->d2xyzdxideta_map[p].zero();
            this->d2xyzdeta2_map[p].zero();
          }

        // compute x, dxdxi at the quadrature points
        for (unsigned int i=0; i<this->psi_map.size(); i++) // sum over the nodes
          {
            const Point& side_point = side->point(i);

            for (unsigned int p=0; p<n_qp; p++) // for each quadrature point...
              {
                this->xyz[p].add_scaled         (side_point, this->psi_map[i][p]);
                this->dxyzdxi_map[p].add_scaled (side_point, this->dpsidxi_map[i][p]);
                this->dxyzdeta_map[p].add_scaled(side_point, this->dpsideta_map[i][p]);
                this->d2xyzdxi2_map[p].add_scaled   (side_point, this->d2psidxi2_map[i][p]);
                this->d2xyzdxideta_map[p].add_scaled(side_point, this->d2psidxideta_map[i][p]);
                this->d2xyzdeta2_map[p].add_scaled  (side_point, this->d2psideta2_map[i][p]);
              }
          }

        // Compute the tangents, normal, and curvature at the quadrature point
        for (unsigned int p=0; p<n_qp; p++)
          {
            const Point n  = this->dxyzdxi_map[p].cross(this->dxyzdeta_map[p]);
            this->normals[p]     = n.unit();
            this->tangents[p][0] = this->dxyzdxi_map[p].unit();
            this->tangents[p][1] = n.cross(this->dxyzdxi_map[p]).unit();

            // Compute curvature using the typical nomenclature
            // of the first and second fundamental forms.
            // For reference, see:
            // 1) http://mathworld.wolfram.com/MeanCurvature.html
            //    (note -- they are using inward normal)
            // 2) F.S. Merritt, Mathematics Manual, 1962, McGraw-Hill
            const Real L  = -this->d2xyzdxi2_map[p]    * this->normals[p];
            const Real M  = -this->d2xyzdxideta_map[p] * this->normals[p];
            const Real N  = -this->d2xyzdeta2_map[p]   * this->normals[p];
            const Real E  =  this->dxyzdxi_map[p].size_sq();
            const Real F  =  this->dxyzdxi_map[p]      * this->dxyzdeta_map[p];
            const Real G  =  this->dxyzdeta_map[p].size_sq();

            const Real numerator   = E*N -2.*F*M + G*L;
            const Real denominator = E*G - F*F;
            libmesh_assert_not_equal_to (denominator, 0.);
            curvatures[p] = 0.5*numerator/denominator;
          }

        // compute the jacobian at the quadrature points, see
        // http://sp81.msi.umn.edu:999/fluent/fidap/help/theory/thtoc.htm
        for (unsigned int p=0; p<n_qp; p++)
          {
            const Real g11 = (dxdxi_map(p)*dxdxi_map(p) +
                              dydxi_map(p)*dydxi_map(p) +
                              dzdxi_map(p)*dzdxi_map(p));

            const Real g12 = (dxdxi_map(p)*dxdeta_map(p) +
                              dydxi_map(p)*dydeta_map(p) +
                              dzdxi_map(p)*dzdeta_map(p));

            const Real g21 = g12;

            const Real g22 = (dxdeta_map(p)*dxdeta_map(p) +
                              dydeta_map(p)*dydeta_map(p) +
                              dzdeta_map(p)*dzdeta_map(p));


            const Real the_jac = std::sqrt(g11*g22 - g12*g21);

            libmesh_assert_greater (the_jac, 0.);

            this->JxW[p] = the_jac*qw[p];
          }

        // done computing the map
        break;
      }


    default:
      libmesh_error_msg("Invalid dimension dim = " << dim);
    }
  STOP_LOG("compute_face_map()", "FEMap");
}
Exemple #2
0
void FEMap::compute_single_point_map(const unsigned int dim,
				     const std::vector<Real>& qw,
				     const Elem* elem,
				     unsigned int p)
{
  libmesh_assert(elem);

  switch (dim)
    {
      //--------------------------------------------------------------------
      // 0D
    case 0:
      {
	xyz[p] = elem->point(0);
        jac[p] = 1.0;
	JxW[p] = qw[p];
        break;
      }

      //--------------------------------------------------------------------
      // 1D
    case 1:
      {
	// Clear the entities that will be summed
	xyz[p].zero();
	dxyzdxi_map[p].zero();
#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
	d2xyzdxi2_map[p].zero();
#endif

	// compute x, dx, d2x at the quadrature point
	for (unsigned int i=0; i<phi_map.size(); i++) // sum over the nodes
	  {
	    // Reference to the point, helps eliminate
	    // exessive temporaries in the inner loop
	    const Point& elem_point = elem->point(i);

	    xyz[p].add_scaled          (elem_point, phi_map[i][p]    );
	    dxyzdxi_map[p].add_scaled  (elem_point, dphidxi_map[i][p]);
#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
	    d2xyzdxi2_map[p].add_scaled(elem_point, d2phidxi2_map[i][p]);
#endif
	  }

	// Compute the jacobian
	//
	// 1D elements can live in 2D or 3D space.
	// The transformation matrix from local->global
	// coordinates is
	//
	// T = | dx/dxi |
	//     | dy/dxi |
	//     | dz/dxi |
	//
	// The generalized determinant of T (from the
	// so-called "normal" eqns.) is
	// jac = "det(T)" = sqrt(det(T'T))
	//
	// where T'= transpose of T, so
	//
	// jac = sqrt( (dx/dxi)^2 + (dy/dxi)^2 + (dz/dxi)^2 )
	jac[p] = dxyzdxi_map[p].size();

	if (jac[p] <= 0.)
	  {
	    libMesh::err << "ERROR: negative Jacobian: "
		          << jac[p]
                          << " in element "
                          << elem->id()
		          << std::endl;
	    libmesh_error();
	  }

	// The inverse Jacobian entries also come from the
	// generalized inverse of T (see also the 2D element
	// living in 3D code).
	const Real jacm2 = 1./jac[p]/jac[p];
	dxidx_map[p] = jacm2*dxdxi_map(p);
#if LIBMESH_DIM > 1
	dxidy_map[p] = jacm2*dydxi_map(p);
#endif
#if LIBMESH_DIM > 2
	dxidz_map[p] = jacm2*dzdxi_map(p);
#endif

	JxW[p] = jac[p]*qw[p];

	// done computing the map
	break;
      }


      //--------------------------------------------------------------------
      // 2D
    case 2:
      {
	//------------------------------------------------------------------
	// Compute the (x,y) values at the quadrature points,
	// the Jacobian at the quadrature points

	xyz[p].zero();

	dxyzdxi_map[p].zero();
	dxyzdeta_map[p].zero();
#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
	d2xyzdxi2_map[p].zero();
	d2xyzdxideta_map[p].zero();
	d2xyzdeta2_map[p].zero();
#endif


	// compute (x,y) at the quadrature points, derivatives once
	for (unsigned int i=0; i<phi_map.size(); i++) // sum over the nodes
	  {
	    // Reference to the point, helps eliminate
	    // exessive temporaries in the inner loop
	    const Point& elem_point = elem->point(i);

	    xyz[p].add_scaled          (elem_point, phi_map[i][p]     );

	    dxyzdxi_map[p].add_scaled      (elem_point, dphidxi_map[i][p] );
	    dxyzdeta_map[p].add_scaled     (elem_point, dphideta_map[i][p]);
#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
	    d2xyzdxi2_map[p].add_scaled    (elem_point, d2phidxi2_map[i][p]);
	    d2xyzdxideta_map[p].add_scaled (elem_point, d2phidxideta_map[i][p]);
	    d2xyzdeta2_map[p].add_scaled   (elem_point, d2phideta2_map[i][p]);
#endif
	  }

	// compute the jacobian once
	const Real dx_dxi = dxdxi_map(p),
                   dx_deta = dxdeta_map(p),
	           dy_dxi = dydxi_map(p),
                   dy_deta = dydeta_map(p);

#if LIBMESH_DIM == 2
	// Compute the Jacobian.  This assumes the 2D face
	// lives in 2D space
	//
	// Symbolically, the matrix determinant is
	//
	//         | dx/dxi  dx/deta |
	// jac =   | dy/dxi  dy/deta |
	//
	// jac = dx/dxi*dy/deta - dx/deta*dy/dxi
	jac[p] = (dx_dxi*dy_deta - dx_deta*dy_dxi);

	if (jac[p] <= 0.)
	  {
	    libMesh::err << "ERROR: negative Jacobian: "
		          << jac[p]
                          << " in element "
                          << elem->id()
		          << std::endl;
	    libmesh_error();
	  }

	JxW[p] = jac[p]*qw[p];

	// Compute the shape function derivatives wrt x,y at the
	// quadrature points
	const Real inv_jac = 1./jac[p];

	dxidx_map[p]  =  dy_deta*inv_jac; //dxi/dx  =  (1/J)*dy/deta
	dxidy_map[p]  = -dx_deta*inv_jac; //dxi/dy  = -(1/J)*dx/deta
	detadx_map[p] = -dy_dxi* inv_jac; //deta/dx = -(1/J)*dy/dxi
	detady_map[p] =  dx_dxi* inv_jac; //deta/dy =  (1/J)*dx/dxi

	dxidz_map[p] = detadz_map[p] = 0.;
#else

	const Real dz_dxi = dzdxi_map(p),
                   dz_deta = dzdeta_map(p);

	// Compute the Jacobian.  This assumes a 2D face in
	// 3D space.
	//
	// The transformation matrix T from local to global
	// coordinates is
	//
	//         | dx/dxi  dx/deta |
	//     T = | dy/dxi  dy/deta |
	//         | dz/dxi  dz/deta |
	// note det(T' T) = det(T')det(T) = det(T)det(T)
	// so det(T) = std::sqrt(det(T' T))
	//
	//----------------------------------------------
	// Notes:
	//
	//       dX = R dXi -> R'dX = R'R dXi
	// (R^-1)dX =   dXi    [(R'R)^-1 R']dX = dXi
	//
	// so R^-1 = (R'R)^-1 R'
	//
	// and R^-1 R = (R'R)^-1 R'R = I.
	//
	const Real g11 = (dx_dxi*dx_dxi +
			  dy_dxi*dy_dxi +
			  dz_dxi*dz_dxi);

	const Real g12 = (dx_dxi*dx_deta +
			  dy_dxi*dy_deta +
			  dz_dxi*dz_deta);

	const Real g21 = g12;

	const Real g22 = (dx_deta*dx_deta +
			  dy_deta*dy_deta +
			  dz_deta*dz_deta);

	const Real det = (g11*g22 - g12*g21);

	if (det <= 0.)
	  {
	    libMesh::err << "ERROR: negative Jacobian! "
                          << " in element "
                          << elem->id()
		          << std::endl;
	    libmesh_error();
	  }

	const Real inv_det = 1./det;
	jac[p] = std::sqrt(det);

	JxW[p] = jac[p]*qw[p];

	const Real g11inv =  g22*inv_det;
	const Real g12inv = -g12*inv_det;
	const Real g21inv = -g21*inv_det;
	const Real g22inv =  g11*inv_det;

	dxidx_map[p]  = g11inv*dx_dxi + g12inv*dx_deta;
	dxidy_map[p]  = g11inv*dy_dxi + g12inv*dy_deta;
	dxidz_map[p]  = g11inv*dz_dxi + g12inv*dz_deta;

	detadx_map[p] = g21inv*dx_dxi + g22inv*dx_deta;
	detady_map[p] = g21inv*dy_dxi + g22inv*dy_deta;
	detadz_map[p] = g21inv*dz_dxi + g22inv*dz_deta;

#endif
	// done computing the map
	break;
      }



      //--------------------------------------------------------------------
      // 3D
    case 3:
      {
	//------------------------------------------------------------------
	// Compute the (x,y,z) values at the quadrature points,
	// the Jacobian at the quadrature point

	// Clear the entities that will be summed
	xyz[p].zero           ();
	dxyzdxi_map[p].zero   ();
	dxyzdeta_map[p].zero  ();
	dxyzdzeta_map[p].zero ();
#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
	d2xyzdxi2_map[p].zero();
	d2xyzdxideta_map[p].zero();
	d2xyzdxidzeta_map[p].zero();
	d2xyzdeta2_map[p].zero();
	d2xyzdetadzeta_map[p].zero();
	d2xyzdzeta2_map[p].zero();
#endif


	// compute (x,y,z) at the quadrature points,
        // dxdxi,   dydxi,   dzdxi,
	// dxdeta,  dydeta,  dzdeta,
	// dxdzeta, dydzeta, dzdzeta  all once
	for (unsigned int i=0; i<phi_map.size(); i++) // sum over the nodes
	  {
	    // Reference to the point, helps eliminate
	    // exessive temporaries in the inner loop
	    const Point& elem_point = elem->point(i);

	    xyz[p].add_scaled           (elem_point, phi_map[i][p]      );
	    dxyzdxi_map[p].add_scaled   (elem_point, dphidxi_map[i][p]  );
	    dxyzdeta_map[p].add_scaled  (elem_point, dphideta_map[i][p] );
	    dxyzdzeta_map[p].add_scaled (elem_point, dphidzeta_map[i][p]);
#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
	    d2xyzdxi2_map[p].add_scaled      (elem_point,
					       d2phidxi2_map[i][p]);
	    d2xyzdxideta_map[p].add_scaled   (elem_point,
					       d2phidxideta_map[i][p]);
	    d2xyzdxidzeta_map[p].add_scaled  (elem_point,
					       d2phidxidzeta_map[i][p]);
	    d2xyzdeta2_map[p].add_scaled     (elem_point,
					       d2phideta2_map[i][p]);
	    d2xyzdetadzeta_map[p].add_scaled (elem_point,
					       d2phidetadzeta_map[i][p]);
	    d2xyzdzeta2_map[p].add_scaled    (elem_point,
					       d2phidzeta2_map[i][p]);
#endif
	  }

	// compute the jacobian
	const Real
	  dx_dxi   = dxdxi_map(p),   dy_dxi   = dydxi_map(p),   dz_dxi   = dzdxi_map(p),
	  dx_deta  = dxdeta_map(p),  dy_deta  = dydeta_map(p),  dz_deta  = dzdeta_map(p),
	  dx_dzeta = dxdzeta_map(p), dy_dzeta = dydzeta_map(p), dz_dzeta = dzdzeta_map(p);

	// Symbolically, the matrix determinant is
	//
	//         | dx/dxi   dy/dxi   dz/dxi   |
	// jac =   | dx/deta  dy/deta  dz/deta  |
	//         | dx/dzeta dy/dzeta dz/dzeta |
	//
	// jac = dx/dxi*(dy/deta*dz/dzeta - dz/deta*dy/dzeta) +
	//       dy/dxi*(dz/deta*dx/dzeta - dx/deta*dz/dzeta) +
	//       dz/dxi*(dx/deta*dy/dzeta - dy/deta*dx/dzeta)

	jac[p] = (dx_dxi*(dy_deta*dz_dzeta - dz_deta*dy_dzeta)  +
		  dy_dxi*(dz_deta*dx_dzeta - dx_deta*dz_dzeta)  +
		  dz_dxi*(dx_deta*dy_dzeta - dy_deta*dx_dzeta));

	if (jac[p] <= 0.)
	  {
	    libMesh::err << "ERROR: negative Jacobian: "
		          << jac[p]
                          << " in element "
                          << elem->id()
		          << std::endl;
	    libmesh_error();
	  }

	JxW[p] = jac[p]*qw[p];

	    // Compute the shape function derivatives wrt x,y at the
	    // quadrature points
	const Real inv_jac  = 1./jac[p];

	dxidx_map[p]   = (dy_deta*dz_dzeta - dz_deta*dy_dzeta)*inv_jac;
	dxidy_map[p]   = (dz_deta*dx_dzeta - dx_deta*dz_dzeta)*inv_jac;
	dxidz_map[p]   = (dx_deta*dy_dzeta - dy_deta*dx_dzeta)*inv_jac;

	detadx_map[p]  = (dz_dxi*dy_dzeta  - dy_dxi*dz_dzeta )*inv_jac;
	detady_map[p]  = (dx_dxi*dz_dzeta  - dz_dxi*dx_dzeta )*inv_jac;
	detadz_map[p]  = (dy_dxi*dx_dzeta  - dx_dxi*dy_dzeta )*inv_jac;

	dzetadx_map[p] = (dy_dxi*dz_deta   - dz_dxi*dy_deta  )*inv_jac;
	dzetady_map[p] = (dz_dxi*dx_deta   - dx_dxi*dz_deta  )*inv_jac;
	dzetadz_map[p] = (dx_dxi*dy_deta   - dy_dxi*dx_deta  )*inv_jac;

	// done computing the map
	break;
      }

    default:
      libmesh_error();
    }
}