NSF Postdoctoral Research
A set of C++ code developed by Andrew E. Slaughter
fem/examples/example5.cpp

A test function for testing the HeatEq class.

This class implements Example 8-1 from Bhatti (2005; p. 552). Run the program with the --help flag for a complete list of run-time options.

// Standard library includes  
#include <iostream>
#include <algorithm>
#include <math.h>
#include <stdio.h>
#include <functional>

// BOOST includes
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>

// Include my fem library
#include "fem/include.h"
using namespace SlaughterFEM;

// Include my common library
#include "common/include.h"
using namespace SlaughterCommon;

// libMesh
#include <libmesh.h>
#include <libmesh_common.h>

#include <exodusII_io.h>
#include <vtk_io.h>

#include <mesh.h>
#include <mesh_generation.h>

#include <equation_systems.h>
#include <transient_system.h>
#include <nonlinear_implicit_system.h>
#include <explicit_system.h>

#include <fe.h>
#include <fe_type.h>
#include <quadrature_gauss.h>
#include <dof_map.h>
#include <sparse_matrix.h>
#include <numeric_vector.h>
#include <dense_matrix.h>
#include <dense_vector.h>

#include <point_locator_tree.h>
  
// Bring in everything from the libMesh namespace
using namespace libMesh;

// A class for containing the nodal data
class EqVolAvgData : public EqDataBase{
   
   public:
   
      // Class constructor
      EqVolAvgData(EquationSystems& sys) : EqDataBase(sys, "eq_data") : velocity_linker(sys){
         add_variable("epsilon");      // volume fraction
         //add_variable("tau_1");         // first stabilization term
         //add_variable("element_length"); // element length, Eq. 69, h
         //add_variable("P_alpha");    // stablization term, Eq. 75
         
         get_system().init();       // initialize the data storage system
      }
         
      // This is called when the solution is updated
      void value(DenseVector<Number>& output, const Point& p, const Real){
         
         // Gather the time from the system
         Real t = get_system().time;
         
         // Return the values of k and cp
         output.resize(2);
         output(0) = epsilon(p);
         output(1) = tau_1(p);
         output(2) = element_length(p);
      }     

      


      // A function for returning the velocity, from the momentum equation
      DenseVector<Number> velocity(const Point& p){
         
         // Get a reference to the system object for the Momentum equation
         TransientNonlinearImplicitSystem& momentum_system = 
            eq_sys.get_system<TransientNonlinearImplicitSystem>("momentum");
         
         // Get the number of dimensions
         const unsigned int dim = dimension();  
                        
         // Collect the velocity vector components
         DenseVector<Number> s(dim);
         for (int d = 0; d < dim; d++){
            s(d) = momentum_system.point_value(d, p);
         }
         
         // Return the vector
         return s;
      }
      
      vector<Number> velocity(const Point& p){
         
         DenseVector<Number> a = velocity(p);
         vector<Number> b;
         for (unsigned int i = 0; i < a.size(); i++){
            b.push_back(a(i));
         }
         
         return b;
      }
      
      // A function for returning the volume fraction
      Number epsilon(const Point& p){
         return 1;
      }

      // Returns the \tau_1 value for the advective stabilization term (Eq. 63)
      Number tau_1(const Point& p){
         
         // Indicate the use of pow and min function from std library
         using std::pow;
         using std::min;

         // Collect the constants
         Number Da = eq_sys.parameters.get<Number>("Da");
         Number Pr = eq_sys.parameters.get<Number>("Pr");         
         
         // Collect the data evaluated at current point
         Number eps = epsilon(p);
         Number h = element_length(p);
         DenseVector<Number> v = velocity(p);

         // Compute the first \tau value
         Number tau_1a = pow(eps, 2) / pow((1-eps), 2) * (Da / Pr);
         
         // Compute the \tau_{SUPG} value, Eq. 64
         Number v_norm = v.l2_norm();        // ||v^h||
         Number Re = v_norm * h / (2 * Pr);     // Re_v, Eq. 67
         
         // z(Re) of Eq. 70
         Number zRe;
         if (Re >= 0 && Re <= 3){
            zRe = Re/3;
         } else {
            zRe = 1;
         }        

         // The \tau_{SUPG} value
         Number tau_1b = eps * h / (2 * v_norm) * zRe;
         
         // Return the minimum of the two values computed
         return min(tau_1a, tau_1b);
      }
      
      // Returns the element length, Eq. 69
      Number element_length(Elem* elem){
            
         // Get the number of dimensions
         const unsigned int dim = dimension();  
            
         // Get the variable index of the element length
         unsigned int idx = get_system().variable_number("element_length");   
            
         // Get the FE type for the element length variable
         FEType fe_type = get_system().variable_type(idx);

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

         // A Gauss quadrature rule for numerical integration.
         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 shape functions evaluated at the quadrature points.
         const std::vector<std::vector<RealGradient> >& dN = fe->get_dphi();
                  
         // Re-initialize the fe system for this element
         fe->reinit(elem);
         
         // Initialize the element length parameter, h
         Number h = 0;
         
         // Get a vector of the points for the quadrature rule
         vector<Point>& pvec = qrule.get_points();
         
         /* Compute the element length at the Gauss points, this 
          * differs from Eq. 69 which uses nodes.
          */ 
          
         // Loop over Gauss points (nodal summation of Eq. 69)
         for (unsigned int qp = 0; qp < qrule.n_points(); qp++){
            
            // Get the velocity vector
            DenseVector<Number> s = velocity(pvec[qp]);
      
            // Compute the L2-norm of the velocity vector
            Number s_norm = s.l2_norm();
            
            // Loop over shape functions
            for (unsigned int i = 0; i < dN.size(); i++){
            
               // If zero velocity, then element length is set to 0
               if (s_norm == 0 ){
                  h = 0;
               
               // Else compute the element length
               } else {
                  
                  // Dot product, using normalized velocity
                  for (int d = 0; d < dim; d++){
                     h += 2 * dN[i][qp](d) * s(d)/s_norm;
                  }
               }
            }
         }
         
         // Return the value of the element length
         return h;   
      }

      // Returns the P_alpha stabilization term, Eq. 75
      Number P_alpha(const Point& p){
            
         // Get the number of dimensions
         const unsigned int dim = dimension();  
            
         // Get the variable index of the element length
         unsigned int idx = get_system().variable_number("element_length");   
            
         // Get the FE type for the element length variable
         FEType fe_type = get_system().variable_type(idx);

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

         // A Gauss quadrature rule for numerical integration.
         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 shape functions evaluated at the quadrature points.
         const std::vector<std::vector<RealGradient> >& dN = fe->get_dphi();
         
         // Get a reference to the mesh
         MeshBase& mesh = eq_sys.get_mesh();
         
         // Determine the element that contains the point
         PointLocatorTree locate(mesh);
         
         // Locate the element
         const Elem* elem = locate(p);
         
         // Re-initialize the fe system for this element
         fe->reinit(elem);
         
         // Initialize the element length parameter, h
         Number P = 0;
         
         // Get a vector of the points for the quadrature rule
         vector<Point>& pvec = qrule.get_points();
         
         // Loop over Gauss points (nodal summation of Eq. 69)
         for (unsigned int qp = 0; qp < qrule.n_points(); qp++){
            
            // Gather the necessary components
            Number eps = epsilon(pvec[qp]);
            Number tau = tau_1(pvec[qp]);
            DenseVector<Number> v = velocity(pvec[qp]);
            
            // Loop over shape functions
            for (unsigned int i = 0; i < dN.size(); i++){
            
               // Dot product, using normalized velocity
               for (int d = 0; d < dim; d++){
                  P += (1 / eps) * tau * v(d) * dN[i][qp](d);
               }
               
            }
         }
         
         // Return the value P_{\alpha}^e
         return P;   
      }
   
   private:
      EqVariableLinker velocity_linker;

};

// Function for initializing Momentum Eq.
void momentum_init_velocity(DenseVector<Number>& output, const Point&, const Real){
   output(0) = 0;
   output(1) = 2;
}

// Function for initializing Energy Eq.
void energy_init_enthalpy(DenseVector<Number>& output, const Point&, const Real){
   output(0) = 0;
}

// Function prototype for assembly of Energy Eq.
void energy_assemble(EquationSystems& eq_sys, const std::string& system_name);


// Initialize Momentum Eq.
void init_function(EquationSystems& es, const std::string& system_name){
   
   // Get a reference to the system
   TransientNonlinearImplicitSystem& system =
     es.get_system<TransientNonlinearImplicitSystem>(system_name);

   // Create a function acceptable for solution projection
   boost::function< void (DenseVector<Number>&, const Point&, const Real)> fptr;
   
   // Link the correct function
   if (system_name.compare("momentum") == 0){
      fptr = momentum_init_velocity;
   
   } else if (system_name.compare("energy") == 0){
      fptr = energy_init_enthalpy;
   }

   // Convert into a libmesh acceptable format
   MyAnalyticFunction<Number> fobj(fptr);

   // Project the solution 
   system.project_solution(&fobj);
}
        

// Begin main function
int main (int argc, char** argv){

    // Initialize libraries
    LibMeshInit init (argc, argv);

   // Generate a mesh object
   MyMesh mesh;
   
   // Create a 2D grid
   MeshTools::Generation::build_square(mesh, 1, 1, 0., 1, 0., 1, QUAD4);
   mesh.all_first_order();
   Order order = FIRST;

   // Create an equation system
   EquationSystems eq_sys(mesh); 

   // Add constants
   eq_sys.parameters.set<Number>("Da") = 1;
   eq_sys.parameters.set<Number>("Pr") = 1;
   eq_sys.parameters.set<Number>("dt") = 0.01;

   // Create momentum equation
   TransientNonlinearImplicitSystem& momentum =
      eq_sys.add_system<TransientNonlinearImplicitSystem>("momentum");

   // Add 2D velocity variables
   momentum.add_variable("vx", order);
   momentum.add_variable("vy", order);
   
   // Attach initialization function
   momentum.attach_init_function(init_function);
   momentum.init();

   // Create the energy equation
   TransientNonlinearImplicitSystem& energy =
      eq_sys.add_system<TransientNonlinearImplicitSystem>("energy");
   
   // Add the enthalpy
   energy.add_variable("h", order);
   
   // Attach initialization function
   energy.attach_init_function(init_function);
   
   // Attach assembly function
   energy.attach_assemble_function(energy_assemble);

   // Initialize the energy
   energy.init();

   // Create the data class (Initializes upon creation)
   EqData data(eq_sys);
   
   // Project the solution at time t = 0
   data.update_solution(0);
   
   // Output the data
   ExodusII_IO(mesh).write_equation_systems("example5.ex2", eq_sys);

   // Call the assembly function for testing
   energy_assemble(eq_sys, "energy");



} // end main


// Energy equation assembly function
// Equation references are from Zabaras and Samanta, 2004
void energy_assemble(EquationSystems& eq_sys, const std::string& system_name){

   // Check that we are assembling the correct system
   libmesh_assert (system_name == "energy");

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

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

   // Get a reference to the system object for the Energy equation
   TransientNonlinearImplicitSystem& system = 
      eq_sys.get_system<TransientNonlinearImplicitSystem>(system_name);
      
   // Get a reference to the system object for the Momentum equation (velocity)
   TransientNonlinearImplicitSystem& momentum_system = 
      eq_sys.get_system<TransientNonlinearImplicitSystem>("momentum");
         
   // Get a constant reference to 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. 
   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> >& N = fe->get_phi();
   const std::vector<std::vector<Real> >& N_face = fe_face->get_phi();

   // Element shape function gradients evaluated at quadrature points
   const std::vector<std::vector<RealGradient> >& dN = 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.
   const DofMap& dof_map = system.get_dof_map();

   // Define data structures to contain the element matrix
   // and right-hand-side vector contribution (Eq. 107)  
   DenseMatrix<Number> Me;       // [\hat{M} + \hat{M}_{\delta}]
   DenseMatrix<Number> Ne;       // [\hat{N} + \hat{N}_{\delta}]
   DenseMatrix<Number> Ke;       // [\hat{K} + \hat{K}_{\delta}]
   DenseVector<Number> Fe;       // [\hat{F} + \hat{F}_{\delta}]
   //DenseVector<Number> Fe_old;    // element force vector (previous time)
   DenseVector<Number> h_old;    // element enthalpy vector (previous time)
   
   DenseVector<Number> v_old;    // element velocity vector (previous time)

   DenseMatrix<Number> Mstar;    // general time integration stiffness matrix (Eq. 125)
   DenseVector<Number> R;        // general time integration force vector (Eq. 126)

   // Storage for the degree of freedom indices
   std::vector<unsigned int> dof_indices;

   // Here we extract the parameters that will be needed
   const Real dt = eq_sys.parameters.get<Real>("dt");    // time step
   Real time = system.time;         // current time

   // Loop over all the elements in the mesh that are on local processor
   MeshBase::const_element_iterator       el     = mesh.active_local_elements_begin();
   const MeshBase::const_element_iterator end_el = mesh.active_local_elements_end(); 

   for ( ; el != end_el; ++el){    

      // Pointer to the element current element
      const Elem* elem = *el;

      // Get the degree of freedom indices for the current element
      dof_map.dof_indices(elem, dof_indices);

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

      // Zero the element matrices and vectors
      Me.resize (dof_indices.size(), dof_indices.size());
      Ne.resize (dof_indices.size(), dof_indices.size());
      Ke.resize (dof_indices.size(), dof_indices.size());
      Fe.resize (dof_indices.size());
      h_old.resize (dof_indices.size());

      // Compute the RHS and mass and stiffness matrix for this element (Me)
      for (unsigned int qp = 0; qp < qrule.n_points(); qp++){
         
         // Get the velocity at this Gauss point (std::vector format)
         vector v(3,0); // Need to get this from my data class
         
         for (unsigned int i = 0; i < N.size(); i++){
            
            // This is not correct
            Fe(i) += 0; 

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

               Me(i,j) += JxW[qp]*(N[i][qp]) * N[j][qp]);  
               Ne(i,j) += JxW[qp]*(N[i][qp]*v[j]*dN[j][qp]);   
            }
         }
      }
/*   
       // BOUNDARY CONDITIONS    
      // Loop through each side of the element for applying boundary conditions
      for (unsigned int s = 0; s < elem->n_sides(); s++){
         
         // Only consider the side if it does not have a neighbor
         if (elem->neighbor(s) == NULL){
            
            // Pointer to current element side
            const AutoPtr<Elem> side = elem->side(s);
               
            // Boundary ID of the current side
            int boundary_id = (mesh.boundary_info)->boundary_id(elem, s);

            // Get index of the boundary class with the same id
            // this vector is empty if there is no match and only
            // contains a single value if there is a match
            std::vector<int> idx = get_boundary_index(boundary_id);  
            
            // Continue of there is a match                 
            if(!idx.empty()){                   
                     
               // Compute the shape function values on the element face
               fe_face->reinit(elem, s);
                                 
               // Create a shared pointer to the boundary class   
               boost::shared_ptr<HeatEqBoundaryBase> ptr = bc_ptrs[idx[0]];
               
               // Determine the type of boundary considered
               std::string type = ptr->type;

               // Loop through quadrature points
               for (unsigned int qp = 0; qp < qface.n_points(); qp++){

                  // DIRICHLET (libMesh version; handled at initialization)
                  if(type.compare("dirichlet") == 0){
                     // The dirichlet conditions are handled at initlization
                     // but I don't want to throw an error if they are
                     // encountered, so just do nothing
                     
                  // NEUMANN condition
                  } else if(type.compare("neumann") == 0){

                     // Current and past flux values              
                     const Number q = ptr->q(qface_points[qp], time);
                     const Number q_old = ptr->q(qface_points[qp], time - dt);
                           
                     // Add values to Fe                 
                     for (unsigned int i = 0; i < psi.size(); i++){
                        Fe(i)      += JxW_face[qp] * q * psi[i][qp];
                        Fe_old(i) += JxW_face[qp] * q_old * psi[i][qp];    
                     }  

                  // CONVECTION boundary
                  } else if(type.compare("convection") == 0){  

                     // Current and past h and T_inf
                     const Number h         = ptr->h(qface_points[qp], time);
                     const Number h_old    = ptr->h(qface_points[qp], time - dt);
                     const Number Tinf   = ptr->Tinf(qface_points[qp], time);
                     const Number Tinf_old = ptr->Tinf(qface_points[qp], time - dt);
                     
                     // Add values to Ke and Fe                
                     for (unsigned int i = 0; i < psi.size(); i++){
                        Fe(i)      += (1) * JxW_face[qp] * h * Tinf * psi[i][qp];
                        Fe_old(i) += (1) * JxW_face[qp] * h_old * Tinf_old * psi[i][qp];
                        
                        for (unsigned int j = 0; j < psi.size(); j++){
                           Ke(i,j) += JxW_face[qp] * psi[i][qp] * h * psi[j][qp];

                        }     
                     }
            
                  // Un-registerd type    
                  } else {
                     printf("WARNING! The boundary type, %s, was not understood!\n", type.c_str());
               
                  }  // (end) type.compare(...) statemenst  
               } //(end) for (int qp = 0; qp < qface.n_points(); qp++)  
            } // (end) if(!idx.empty)
         } // (end) if (elem->neighbor(s) == NULL){
      } // (end) for (int s = 0; s < elem->n_sides(); s++)  

      // Zero the pervious time-step temperature vector for this element
      u_old.resize(dof_indices.size());
      
      // Gather the temperatures at the nodes
      for (unsigned int i = 0; i < psi.size(); i++){
         u_old(i) = system.old_solution(dof_indices[i]);
      }

      // Build K_hat and F_hat (appends existing)
      K_hat.resize(dof_indices.size(), dof_indices.size());
      F_hat.resize(dof_indices.size());
      build_stiffness_and_rhs(K_hat, F_hat, Me, Ke, Fe_old, Fe, u_old, dt, theta);
      
      // Applies the dirichlet constraints to K_hat and F_hat
      dof_map.heterogenously_constrain_element_matrix_and_vector(K_hat, F_hat, dof_indices);
      
      // Apply the local components to the global K and F
      system.matrix->add_matrix(K_hat, dof_indices);
      system.rhs->add_vector(F_hat, dof_indices);  
*/    
   } // (end) for ( ; el != end_el; ++el)

} // (end) assemble()




 All Classes Namespaces Files Functions Variables Typedefs