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)