def integrateAndSaveProfile(self, R, y0, dr, 
                                epsfrac, epsabs,drmin, *eqn_args):
        """
        Integrate the bubble profile, saving the output in an array.
        
        Parameters
        ----------
        R: array_like 
            The array of points at which we want to save the profile.
        y0 : float 
            The starting values [phi(r0), dphi(r0)].
        dr : float
            Starting stepsize.
        epsfrac, epsabs : float 
            The error tolerances used for integration. This
            is fed into :func:`helper_functions.rkqs`.
        drmin : float
            The smallest allowed stepsize.
        eqn_args : tuple
            Extra arguments to pass to :func:`equationOfMotion`. Useful for
            subclasses.
 
        Returns
        -------
        R, Phi, dPhi : array_like 
            Radii and field values which make up the bubble profile.
        Rerr : float or None
            The first value of `r` at which ``dr < drmin``, or `None` if
            ``dr >= drmin`` always.

        Notes
        -----
        Subclasses can use this function without overriding it even if the
        subclass uses more fields/values in its equation of motion (i.e., 
        ``len(y0) > 2``). This is accomplished by setting the class variable
        `profile_rval` to a different named tuple type with more than four
        inputs. The first three should always be *R, Phi, dPhi*, and the last
        one should be *Rerr*, but additional values can be stuck in between.
        """
        N = len(R)
        R, r0 = np.array(R), R[0]
        Yout = np.zeros((N,len(y0)))
        Yout[0] = y0
        # dY is the ODE that we use
        def dY(y,r,args=eqn_args): 
            return self.equationOfMotion(y,r,*args)
        dydr0 = dY(y0, r0)
        Rerr = None
                
        i = 1
        while i < N:
            dy, dr, drnext = rkqs(y0, dydr0, r0, dY, dr, epsfrac, epsabs)
            if (dr >= drmin):    
                r1 = r0 + dr
                y1 = y0 + dy
            else:
                y1 = y0 + dy*drmin/dr
                dr = drnext = drmin
                r1 = r0 + dr
                if Rerr is not None: Rerr = r1
            dydr1 = dY(y1,r1)
            # Fill the arrays, if necessary
            if (r0 < R[i] <= r1):
                f = cubicInterpFunction(y0, dr*dydr0, y1, dr*dydr1)
                while (i < N and r0 < R[i] <= r1):
                    x = (R[i]-r0)/dr
                    Yout[i] = f(x)
                    i += 1
            
            # Advance the integration variables
            r0,y0,dydr0 = r1,y1,dydr1
            dr = drnext
        
        rval = (R,)+tuple(Yout.T)+eqn_args+(Rerr,)
        return self.profile_rval(*rval)
    def integrateProfile(self, r0, y0, dr0, 
                         epsfrac, epsabs, drmin, rmax, *eqn_args):
        R"""
        Integrate the bubble wall equation:
        
        .. math::
          \frac{d^2\phi}{dr^2} + \frac{\alpha}{r}\frac{d\phi}{dr} =
          \frac{dV}{d\phi}.
          
        The integration will stop when it either overshoots or undershoots
        the false vacuum minimum, or when it converges upon the false vacuum
        minimum.

        Parameters
        ----------
        r0 : float 
            The starting radius for the integration.
        y0 : array_like
            The starting values [phi(r0), dphi(r0)].
        dr0 : float
            The starting integration stepsize.
        epsfrac, epsabs : float
            The error tolerances used for integration. This is fed into 
            :func:`helper_functions.rkqs` and is used to test for convergence.
        drmin : float 
            The minimum allowed value of `dr` before raising an error.
        rmax : float
            The maximum allowed value of `r-r0` before raising an error.
        eqn_args : tuple
            Extra arguments to pass to :func:`equationOfMotion`. Useful for
            subclasses.
          
        Returns
        -------
        r : float
            The final radius.
        y : array_like
            The final field values [phi, dphi]
        convergence_type : str
            Either 'overshoot', 'undershoot', or 'converged'.
        
        Raises
        ------
        helper_functions.IntegrationError
        """
        dr = dr0
        # dY is the ODE that we use
        def dY(y,r,args=eqn_args): 
            return self.equationOfMotion(y,r,*args)
        dydr0 = dY(y0, r0)
        ysign = np.sign(y0[0]-self.phi_metaMin) 
            # positive means we're heading down, negative means heading up.
        rmax += r0
        
        i = 1
        convergence_type = None
        while True:
            dy, dr, drnext = rkqs(y0, dydr0, r0, dY, dr, epsfrac, epsabs)
            r1 = r0 + dr
            y1 = y0 + dy
            dydr1 = dY(y1,r1)
        
            # Check for completion
            if (r1 > rmax):
                raise IntegrationError("r > rmax")
            elif (dr < drmin):
                raise IntegrationError("dr < drmin")
            elif( (abs(y1 - np.array([self.phi_metaMin,0])) < 3*epsabs).all() ):
                r,y = r1,y1
                convergence_type = "converged"
                break
                
            elif( y1[1]*ysign > 0 or (y1[0]-self.phi_metaMin)*ysign < 0 ):
                f = cubicInterpFunction(y0, dr*dydr0, y1, dr*dydr1)
                if(y1[1]*ysign > 0):
                    # Extrapolate to where dphi(r) = 0
                    x = optimize.brentq(lambda x: f(x)[1], 0, 1 )
                    convergence_type = "undershoot"
                else:
                    # Extrapolate to where phi(r) = phi_metaMin
                    x = optimize.brentq(lambda x: f(x)[0]-self.phi_metaMin, 0,1)
                    convergence_type = "overshoot"
                r = r0 + dr*x
                y = f(x)
                break
            # Advance the integration variables
            r0,y0,dydr0 = r1,y1,dydr1
            dr = drnext
        # Check convergence for a second time. 
        # The extrapolation in overshoot/undershoot might have gotten us within
        # the acceptable error.
        if (abs(y - np.array([self.phi_metaMin,0])) < 3*epsabs).all():
            convergence_type = "converged"
        return self._integrateProfile_rval(r, y, convergence_type)