Example #1
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
}
Example #2
0
// Stiffness and RHS assembly
// Equation references are from Samanta and Zabaras, 2005
void EnergySystem :: assemble(){

// GENERAL VARIABLES
	// Get a constant reference to the mesh object.
	const MeshBase& mesh = this->get_mesh();

	// The dimension that we are running
	const unsigned int dim = this->ndim();

// FEM THERMODYNAMIC RELATIONSHIPS (ThermoEq Class) 
	// Determine the FEM type (should be same for all ThermoEq variables)
	FEType fe_type_thermo = thermo->variable_type(0);
	
	// Build FE object; accessed via a pointer
	AutoPtr<FEBase> fe_thermo(FEBase::build(dim, fe_type_thermo));

	// Setup a quadrature rule
	QGauss qrule_thermo(dim, fe_type_thermo.default_quadrature_order());
	
	// Link FE and Quadrature
	fe_thermo->attach_quadrature_rule(&qrule_thermo);

	// References to shape functions and derivatives
	const vector<std::vector<Real> >& N_thermo = fe_thermo->get_phi();
	const vector<std::vector<RealGradient> >& B_thermo = fe_thermo->get_dphi();
		
	// Setup a DOF map
	const DofMap& dof_map_thermo = thermo->get_dof_map(); 

// FEM MOMENTUM EQUATION
	// Determine the FEM type 
	FEType fe_type_momentum = momentum->variable_type(0);
	
	// Build FE object; accessed via a pointer
	AutoPtr<FEBase> fe_momentum(FEBase::build(dim, fe_type_momentum));

	// Setup a quadrature rule
	QGauss qrule_momentum(dim, fe_type_momentum.default_quadrature_order());
	
	// Link FE and Quadrature
	fe_momentum->attach_quadrature_rule(&qrule_momentum);

	// References to shape functions and derivatives
	const vector<std::vector<Real> >& N_momentum = fe_momentum->get_phi();
		
	// Setup a DOF map
	const DofMap& dof_map_momentum = momentum->get_dof_map(); 
	
// FEM ENERGY EQ. RELATIONSHIPS
	// Get a constant reference to the Finite Element type
	// for the first (and only) variable in the system.
	FEType fe_type = this->variable_type(0);

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

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

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

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

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

	// Element shape function gradients evaluated at quadrature points
	const vector<std::vector<RealGradient> >& B = fe->get_dphi();

	// The XY locations of the quadrature points used for face integration
	const vector<Point>& qface_points = fe_face->get_xyz();

	// A reference to the \p DofMap objects
	const DofMap& dof_map = this->get_dof_map(); // this system

// DEFINE VECTOR AND MATRIX VARIABLES
	// Define data structures to contain the element matrix
	// and right-hand-side vector contribution (Eq. 107)  
	DenseMatrix<Number> Me;			// [\hat{M} + \hat{M}_{\delta}]
	DenseMatrix<Number> Ne;			// [\hat{N} + \hat{N}_{\delta}]
	DenseMatrix<Number> Ke;			// [\hat{K} + \hat{K}_{\delta}]
	DenseVector<Number> Fe;			// [\hat{F} + \hat{F}_{\delta}]
	
	//DenseVector<Number> Fe_old;		// element force vector (previous time)
	DenseVector<Number> h;			// element enthalpy vector (previous time)
	DenseVector<Number> h_dot;
	//DenseVector<Number> delta_h_dot;
	
	DenseMatrix<Number> Mstar;		// general time integration stiffness matrix (Eq. 125)
	DenseVector<Number> R;			// general time integration force vector (Eq. 126)

	// Storage vectors for the degree of freedom indices 
	std::vector<unsigned int> dof_indices;		// this system (h)
//	std::vector<unsigned int> dof_indices_hdot;		
//	std::vector<unsigned int> dof_indices_deltahdot;		
	std::vector<unsigned int> dof_indices_velocity;	// this system
	std::vector<unsigned int> dof_indices_rho;	// ThermoEq density
	std::vector<unsigned int> dof_indices_tmp;  // ThermoEq temperature
	std::vector<unsigned int> dof_indices_f;  	// ThermoEq liquid fraction
	std::vector<unsigned int> dof_indices_eps;  // ThermoEq epsilon
	
	// Define the necessary constants
	const Number gamma = get_constant<Number>("gamma");
	const Number dt = get_constant<Number>("dt");		// time step
	Real time = this->time;					// current time
	const Number ks = thermo->get_constant<Number>("conductivity_solid");
	const Number kf = thermo->get_constant<Number>("conductivity_fluid");
	const Number cs = thermo->get_constant<Number>("specific_heat_solid");
	const Number cf = thermo->get_constant<Number>("specific_heat_fluid");
	const Number Te = thermo->get_constant<Number>("eutectic_temperature");
	const Number hf = thermo->get_constant<Number>("latent_heat");

	// Index of density variable in ThermoEq system
	const unsigned int rho_idx = thermo->variable_number("density");
	const unsigned int tmp_idx = thermo->variable_number("temperature");
	const unsigned int f_idx   = thermo->variable_number("liquid_mass_fraction");
	const unsigned int eps_idx = thermo->variable_number("epsilon");

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

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

		// Pointer to the element current element
		const Elem* elem = *el;
		
		// Get the degree of freedom indices for the current element
		dof_map.dof_indices(elem, dof_indices, 0);
		//dof_map.dof_indices(elem, dof_indices_hdot, 1);
		//dof_map.dof_indices(elem, dof_indices_deltahdot, 2);
		dof_map_momentum.dof_indices(elem, dof_indices_velocity);		
		dof_map_thermo.dof_indices(elem, dof_indices_rho, rho_idx);
		dof_map_thermo.dof_indices(elem, dof_indices_tmp, tmp_idx);
		dof_map_thermo.dof_indices(elem, dof_indices_f, f_idx);
		dof_map_thermo.dof_indices(elem, dof_indices_eps, eps_idx);

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

		// Zero the element matrices and vectors
		Me.resize (dof_indices.size(), dof_indices.size());		// [\hat{M} + \hat{M}_{\delta}]
		Ne.resize (dof_indices.size(), dof_indices.size());		// 
		Ke.resize (dof_indices.size(), dof_indices.size());
		Fe.resize (dof_indices.size());
		
		// Extract a vector of quadrature x,y,z coordinates
		const vector<Point> qp_vec = fe->get_xyz(); 

		// Compute the element length, h
		Number elem_length = thermo->element_length(elem);

		// Compute the RHS and mass and stiffness matrix for this element (Me)
		for (unsigned int qp = 0; qp < qrule.n_points(); qp++){
				
			// Get the velocity vector at this point (old value)
			VectorValue<Number> v;
			for (unsigned int i = 0; i < N_momentum.size(); i++){
				for (unsigned int j = 0; j < dim; j++){
					v(j) += N_momentum[i][qp] * momentum->old_solution(dof_indices_velocity[2*i+j]);
				}
			}
		
			// Compute ThermoEq variables; must be mapped from node to quadrature points
			Number T = 0;
			Gradient grad_T;
			Number f = 0;
			Gradient grad_f;			
			Number rho = 0;
			Number rho_old = 0;
			Number eps = 0;

			for (unsigned int i = 0; i < N_thermo.size(); i++){
				T += N_thermo[i][qp] * thermo->current_solution(dof_indices_tmp[i]);
				grad_T.add_scaled(B_thermo[i][qp], thermo->current_solution(dof_indices_tmp[i]));
				f += N_thermo[i][qp] * thermo->current_solution(dof_indices_f[i]);
				grad_f.add_scaled(B_thermo[i][qp], thermo->current_solution(dof_indices_f[i]));
				rho += N_thermo[i][qp] * thermo->current_solution(dof_indices_rho[i]);
				rho_old += N_thermo[i][qp] * thermo->old_solution(dof_indices_rho[i]);
				eps += N_thermo[i][qp] * thermo->current_solution(dof_indices_eps[i]);
			}

			// Compute EnergySystem variables
			Gradient grad_h;
			for (unsigned int i = 0; i < B.size(); i++){
				grad_h.add_scaled(B[i][qp], this->current_solution(dof_indices[i]));
			}

			// Compute T_{,k}^h v_k^h and f_{,k} v_k^h summation terms for F
			Number Tv = 0;
			Number fv = 0;
			for (unsigned int i = 0; i < dim; i++){
				Tv += grad_T(i) * v(i);
				fv += grad_f(i) * v(i);
			}

			// Compute the time derivative of density
			const Number drho_dt = (rho - rho_old)/dt;

			// Compute alpha term of Eq. 69
			const Number alpha = this->alpha(grad_T, grad_h, f);
			
			// Extract tau_1 stabilization term		
			const Number tau_1 = thermo->tau_1(qp_vec[qp], elem_length);
					
			// Loop through the components and construct matrices
			for (unsigned int i = 0; i < N.size(); i++){
				
				// Compute advective stabilization term (Eq. A, p. 1777)
				const Number d = tau_1 * v * B[i][qp] / f - tau_1 * 1/rho * drho_dt * (1-f)/f * N[i][qp];										
				// Force vector, Eq. 77
				Number F1 = JxW[qp] * (N[i][qp] + d) * rho * (1 - f) * (cf - cs) * Tv;   	
				Number F2 = JxW[qp] * (N[i][qp] + d) * rho * fv * ((cf - cs) * (T - Te) + hf);	
				Number F3 = JxW[qp] * (N[i][qp] + d) * drho_dt * (1 - f) * ((cf - cs) * (T - Te) + hf);	
				Fe(i) += F1 + F2 + F3;

				// Build the stiffness matrices
				for (unsigned int j = 0; j < N.size(); j++){
		
					// Mass matrix, Eq. 108
					Me(i,j) += JxW[qp] * rho * ((N[i][qp] + d) * N[j][qp]); 
					
					// Stiffness matrix one, Ne, Eq. 109
					Ne(i,j) += JxW[qp] * rho * ((N[i][qp] + d) * (v * B[j][qp]));
					
					// Stiffness matrix two, Ke, Eq. 110
					Ke(i,j) += JxW[qp]*((eps*kf + (1 - eps)*ks) * alpha * B[i][qp] * B[j][qp]);
				}
			}
		}
		
		printf("Me:\n");
		Me.print(std::cout);
		
		printf("\nNe:\n");
		Ne.print(std::cout);
		
		printf("\nKe:\n");
		Ke.print(std::cout);
		
		printf("\nFe:\n");
		Fe.print(std::cout);		
	
	
		h.resize(dof_indices.size());
		h_dot.resize(dof_indices.size());
	//	delta_h_dot.resize(dof_indices_deltahdot.size());


		for (unsigned int i = 0; i < dof_indices.size(); i++){
			h(i) = this->old_solution(dof_indices[i]);
			h_dot(i) = this->get_vector("h_dot")(dof_indices[i]);
	//		delta_h_dot(i) = this->old_solution(dof_indices_deltahdot[i]);
		}
		
		this->get_matrix("M").add_matrix(Me, dof_indices);
		this->get_matrix("N").add_matrix(Ne, dof_indices);
		this->get_matrix("K").add_matrix(Ke, dof_indices);
		this->get_vector("F").add_vector(Fe, dof_indices);
		
		Mstar.resize(dof_indices.size(), dof_indices.size());	
		R.resize(dof_indices.size());

		// Me + gamma*dt*(Ke + Ne);
		Mstar.add(1,Me);		
		Mstar.add(gamma*dt,Ke);
		Mstar.add(gamma*dt,Ne);

		this->matrix->add_matrix(Mstar, dof_indices);

		R.add(1,Fe);

		DenseVector<Number> a(dof_indices.size());
		Me.vector_mult(a, h_dot);
		R.add(-1, a);

		DenseMatrix<Number> B(dof_indices.size(), dof_indices.size());
		DenseVector<Number> b(dof_indices.size());
		B.add(1,Ne);
		B.add(1,Ke);

		B.vector_mult(b, h);

		R.add(-1,b);

		this->rhs->add_vector(R, dof_indices);

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

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

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

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

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

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

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

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

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

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

	//update_rhs();


} // (end) assemble()