geff.models.fai_basic

$\newcommand{\bm}[1]{\boldsymbol{#1}}$ Module defining the GEF model "fai_basic" corresponding to fermionic axion inflation without a heuristic scale dependence.

For more details on this model, see e.g., 2109.01651.


The model knows the following variables:

  • time variable: t - cosmic time, $t$
  • dynamical variables:
    • N - $e$-folds, $N$
    • phi, dphi - inflaton amplitude, $\varphi$, and velocity, $\dot{\varphi}$
    • kh - the instability scale, $k_{\rm h}$
    • delta - cumulative electric damping, $\Delta$
    • rhoChi - fermion energy density, $\rho_{\chi}$
  • static variables:
    • a - scale factor, $a$
    • H - Hubble rate, $H$
    • ddphi - inflaton acceleration, $\ddot{\varphi}$
    • E, B, G - gauge-field expectation values, $\langle \bm{E}^2 \rangle$, $\langle \bm{B}^2 \rangle$, -$\langle \bm{E} \cdot \bm{B} \rangle$
    • xi - instability parameter, $\xi$
    • sigmaE, sigmaB - electric and magnetic conductivities, $\sigma_{\rm E}$, $\sigma_{\rm B}$
    • xieff - effective instability parameter, $\xi_{\rm eff}$
    • s - electric damping parameter, $s = \sigma_{\rm E}/(2H)$
  • constants:
    • beta - coupling strength, $\beta$
  • functions:
    • V,dV - inflaton potential, $V(\varphi)$, and its derivative, $V_{,\varphi}(\varphi)$
  • gauge field:
    • GF - tower of gauge bilinears, $\mathcal{F}_{\mathcal X}^{(n)}$

The model expects the following input:

  • phi, dphi - initial data on the inflaton, $\varphi$, $\dot\varphi$
  • rhoChi - initial data on the fermion energy density, $\rho_{\chi}$
  • beta - coupling strength, $\beta$
  • V, dV - potential shape, $V(\varphi)$, $V_{,\varphi}(\varphi)$

The model tracks the following events:

  • end of inflation - terminate solver when $\ddot{a} < 0$
  • negative norms - return an error when $\langle \bm{E}^2 \rangle$ or $\langle \bm{B}^2 \rangle$ are negative
  1r"""
  2$\newcommand{\bm}[1]{\boldsymbol{#1}}$
  3Module defining the GEF model "fai_basic" corresponding to fermionic axion inflation without a heuristic scale dependence.
  4
  5For more details on this model, see e.g., [2109.01651](https://arxiv.org/abs/2109.01651).
  6
  7---
  8
  9The model knows the following variables:
 10* time variable: `t` - *cosmic time*, $t$ 
 11* dynamical variables:
 12    * `N` - *$e$-folds*,  $N$
 13    * `phi`, `dphi` - *inflaton amplitude, $\varphi$, and velocity, $\dot{\varphi}$* 
 14    * `kh` -  *the instability scale, $k_{\rm h}$*
 15    * `delta` - *cumulative electric damping, $\Delta$*
 16    * `rhoChi` - *fermion energy density, $\rho_{\chi}$*
 17* static variables:
 18    * `a` - *scale factor, $a$* 
 19    * `H` - *Hubble rate, $H$* 
 20    * `ddphi` - *inflaton acceleration, $\ddot{\varphi}$*
 21    * `E`, `B`, `G` - *gauge-field expectation values, $\langle \bm{E}^2 \rangle$, $\langle \bm{B}^2 \rangle$, -$\langle \bm{E} \cdot \bm{B} \rangle$*
 22    * `xi` - *instability parameter, $\xi$* 
 23    * `sigmaE`, `sigmaB` - *electric and magnetic conductivities, $\sigma_{\rm E}$, $\sigma_{\rm B}$*
 24    * `xieff` - *effective instability parameter, $\xi_{\rm eff}$*
 25    * `s` - *electric damping parameter, $s = \sigma_{\rm E}/(2H)$*
 26* constants: 
 27    * `beta` - *coupling strength, $\beta$*
 28* functions: 
 29    * `V`,`dV` - *inflaton potential, $V(\varphi)$, and its derivative, $V_{,\varphi}(\varphi)$*
 30* gauge field: 
 31    * `GF` - *tower of gauge bilinears, $\mathcal{F}_{\mathcal X}^{(n)}$*
 32
 33The model expects the following input:
 34* `phi`, `dphi` - *initial data on the inflaton, $\varphi$, $\dot\varphi$*
 35* `rhoChi` - *initial data on the fermion energy density, $\rho_{\chi}$*
 36* `beta` - *coupling strength, $\beta$*
 37* `V`, `dV` - *potential shape, $V(\varphi)$, $V_{,\varphi}(\varphi)$*
 38
 39The model tracks the following events:
 40* end of inflation - terminate solver when $\ddot{a} < 0$
 41* negative norms - return an error when $\langle \bm{E}^2 \rangle$ or  $\langle \bm{B}^2 \rangle$ are negative 
 42"""
 43import numpy as np
 44
 45from geff.bgtypes import t, N, a, H, phi, dphi, ddphi, V, dV, E, B, G, xi, kh, beta, GF, define_var
 46from geff.solver import TerminalEvent, ErrorEvent, GEFSolver
 47from geff.mbm import ModeSolver
 48
 49from geff.utility.eom import (klein_gordon, friedmann, dlnkh_schwinger, ddelta, drhoChi, gauge_field_ode_schwinger,
 50                                        conductivities_collinear, conductivities_mixed, check_accelerated_expansion)
 51from geff.utility.boundary import boundary_fai
 52from geff.utility.general import heaviside
 53from geff.utility.mode import mode_equation_SE_no_scale, damped_bd
 54from geff._docs import generate_docs, docs_models
 55
 56name = "FAI basic"
 57"""The models name."""
 58
 59settings = {"picture":"mixed"}
 60"""The model settings.
 61
 62Possible settings are "mixed", "electric", "magnetic".
 63
 64Determines if conductivities are computed assuming collinear E&M fields 
 65("electric", "magnetic") or not ("mixed").
 66"""
 67
 68# parse settings
 69def define_conductivity():
 70    if settings["picture"]=="mixed":
 71        conductivity = conductivities_mixed
 72    elif settings["picture"]=="electric":
 73        def conductivity(a, H, E, B, G, omega):
 74            return conductivities_collinear(a, H, E, B, G, -1, omega)
 75    elif settings["picture"]=="magnetic":
 76        def conductivity(a, H, E, B, G, omega):
 77            return conductivities_collinear(a, H, E, B, G, 1, omega)
 78    else:
 79        raise KeyError(f"{settings['picture']} is an unknown choice for the setting 'picture'")
 80    return np.vectorize(conductivity)
 81
 82def interpret_settings():
 83    global conductivity
 84    conductivity = define_conductivity()
 85    return
 86
 87#Define all additional variables
 88sigmaE=define_var("sigmaE", 1, 0, "electric damping")
 89sigmaB=define_var("sigmaB", 1, 0, "magnetic damping")
 90delta=define_var("delta", 0, 0, "cumulative electric damping") 
 91xieff=define_var("xieff", 0, 0, "effective instabilty parameter") 
 92s=define_var("s", 0, 0, "electric damping parameter")
 93rhoChi=define_var("rhoChi", 4, 0, "fermion energy density")
 94
 95#Assign quantities to a dictionary, classifying them by their role:
 96quantities={
 97            "time":[t],
 98            "dynamical":[N, phi, dphi, kh, delta, rhoChi],
 99            "static":[a, H, xi, E, B, G, ddphi, sigmaE, sigmaB, xieff, s],
100            "constant":[beta],
101            "function":[V, dV],
102            "gauge":[GF]
103            }
104
105#State which variables require input for initialisation
106model_input = [phi, dphi, rhoChi, beta, V, dV]
107
108#this functions is called upon initialisation of the GEF class
109def define_units(phi, dphi, V, rhoChi):
110    #compute Hubble rate at t0
111    rhoK = 0.5*dphi**2
112    rhoV = V(phi)
113    H0 = friedmann( rhoK, rhoV, rhoChi )
114    
115    omega = H0 #Characteristic frequency is the initial Hubble rate
116    mu = 1. #Charatcterisic amplitude is the Planck mass
117    
118    return omega, mu
119
120#the new function for sys_to_yini in GEFSolver
121def initial_conditions(sys, ntr):
122    yini = np.zeros((ntr+1)*3+6)
123
124    #from the 'input' dictionary
125    yini[0] = sys.N.value
126    yini[1] = sys.phi.value
127    yini[2] = sys.dphi.value
128
129    #needs to be computed
130    sys.initialise("kh")( abs(sys.dphi)*sys.beta )
131    yini[3] = np.log(sys.kh.value)
132
133    #initialise delta and rhoChi
134    yini[4] = sys.delta.value
135    yini[5] = sys.rhoChi.value
136
137    #all gauge-field expectation values are assumed to be 0 at initialisation
138    return yini
139
140#define update_sys for GEFSolver
141def update_values(t, y, sys, atol=1e-20, rtol=1e-6):
142    #spacetime variables
143    sys.t.value = t
144    sys.N.value = y[0]
145    sys.a.value = np.exp(y[0])
146
147    #parse for convenience
148    sys.phi.value = y[1]
149    sys.dphi.value = y[2]
150    sys.kh.value = np.exp(y[3])
151    sys.delta.value = y[4]
152    sys.rhoChi.value = y[5]
153
154    #the gauge-field terms in y are not stored, save these values here
155    sys.E.value = y[6]*np.exp(4*(y[3]-y[0]))
156    sys.B.value = y[7]*np.exp(4*(y[3]-y[0]))
157    sys.G.value = y[8]*np.exp(4*(y[3]-y[0]))
158
159    #Hubble rate
160    sys.H.value = ( friedmann(0.5*sys.dphi**2, sys.V(sys.phi), 
161                                0.5*(sys.E+sys.B)*sys.omega**2, sys.rhoChi*sys.omega**2) )
162
163    #conductivities
164    sigmaE, sigmaB, ks = conductivity(sys.a.value, sys.H.value, sys.E.value,
165                                       sys.B.value, sys.G.value, sys.omega)
166
167    GlobalFerm = heaviside(np.log(ks),np.log(sys.a*sys.H))
168    sys.sigmaE.value = (GlobalFerm*sigmaE)
169    sys.sigmaB.value = (GlobalFerm*sigmaB)
170
171    #boundary term parameters
172    sys.s.value = sys.sigmaE/(2*sys.H)
173    sys.xi.value = sys.beta*(sys.dphi/(2*sys.H))
174    sys.xieff.value = sys.xi + sys.sigmaB/(2*sys.H)
175
176    #acceleration for convenience
177    sys.ddphi.value = klein_gordon(sys.dphi, sys.dV(sys.phi), sys.H, -sys.G*sys.beta*sys.omega**2)
178    return
179
180def compute_timestep(t, y, sys, atol=1e-20, rtol=1e-6):
181    dydt = np.zeros(y.shape)
182
183    #odes for N and phi
184    dydt[0] = sys.H.value
185    dydt[1] = sys.dphi.value
186    dydt[2] = sys.ddphi.value
187
188    #achieving dlnkhdt is monotonous requires some care
189    dlnkhdt = dlnkh_schwinger( sys.kh, sys.dphi, sys.ddphi, sys.beta,
190                                        0., sys.xieff, sys.s, sys.a, sys.H )
191    xieff = sys.xieff.value
192    s = sys.s.value
193    sqrtterm = np.sqrt(xieff**2 + s**2 + s)
194    r = (abs(xieff) + sqrtterm)
195    logfc = y[0] + np.log(r*dydt[0]) 
196    dlnkhdt *= heaviside(dlnkhdt,0)*heaviside(logfc,y[3]*(1-1e-5))
197    dydt[3] = dlnkhdt
198
199    #the other derivatives are straight forwards
200    dydt[4] = ddelta( sys.delta, sys.sigmaE )
201    dydt[5] = drhoChi( sys.rhoChi, sys.E, sys.G, sys.sigmaE, sys.sigmaB, sys.H )
202
203    #compute boundary terms and then the gauge-field bilinear ODEs
204    Fcol = y[6:].shape[0]//3
205    F = y[6:].reshape(Fcol,3)
206    W = boundary_fai(sys.xieff.value, sys.s.value)
207    dFdt = gauge_field_ode_schwinger( F, sys.a, sys.kh, 2*sys.H*sys.xieff,
208                                            sys.sigmaE, sys.delta,
209                                                W, dlnkhdt )
210    #reshape to fit dydt
211    dydt[6:] = dFdt.reshape(Fcol*3)
212
213    return dydt
214
215#Event 1: Track the end of inflation:
216def condition_EndOfInflation(t, y, sys):
217   #compute energy densities
218    rhoV = sys.V(y[1])
219    rhoK = y[2]**2/2
220    rhoEM = 0.5*(y[6]+y[7])*(sys.omega/sys.mu)**2*np.exp(4*(y[3]-y[0]))
221    rhoF = y[5]*(sys.omega/sys.mu)**2
222
223    #compute pressure
224    pV = -rhoV
225    pK = rhoK
226    pEM = 1/3*rhoEM
227    pF = 1/3*rhoF
228
229    return check_accelerated_expansion([rhoV, rhoK, rhoEM, rhoF], [pV, pK, pEM, pF])/(sys.H)**2
230
231def consequence_EndOfInflation(sys, occurance):
232    if occurance:
233        #stop solving once the end of inflation is reached
234        return "finish", {}
235    else:
236        #increase tend given the current Hubble rate
237        tdiff = np.round(5/sys.H, 0)
238        #round again, sometimes floats cause problems in t_span and t_eval.
239        tend  = np.round(sys.t + tdiff, 0)
240        return "proceed", {"tend":tend}
241    
242EndOfInflation = TerminalEvent("End of inflation", condition_EndOfInflation, -1, consequence_EndOfInflation)
243"""Defines the 'End of inflation' event."""
244
245#Event 2: ensure that E^2 and B^2 are positive
246def condition_NegativeNorms(t, y, sys):
247    return min(y[6], y[7])
248    
249NegativeNorms : ErrorEvent = ErrorEvent("Negative norms", condition_NegativeNorms, -1, "Negative value for E^2 or B^2.")
250"""Defines the 'Negative norms' event."""
251
252events = [EndOfInflation, NegativeNorms]
253
254
255
256#gather all information in the solver
257solver = GEFSolver(initial_conditions, update_values, compute_timestep, quantities, events)
258"""The solver used by the GEF model."""
259
260#define mode-by-mode solver
261MbM = ModeSolver(mode_equation_SE_no_scale, {"a":a, "xieff":xieff, "H":H, "sigmaE":sigmaE},
262                         damped_bd, {"a":a, "sigmaE":sigmaE, "delta":delta}, default_atol=1e-5)
263"""The mode solver used by the GEF model."""
264
265
266#define default docs for the above functions
267generate_docs(docs_models.DOCS)
name = 'FAI basic'

The models name.

settings = {'picture': 'mixed'}

The model settings.

Possible settings are "mixed", "electric", "magnetic".

Determines if conductivities are computed assuming collinear E&M fields ("electric", "magnetic") or not ("mixed").

def define_units(phi, dphi, V, rhoChi):
110def define_units(phi, dphi, V, rhoChi):
111    #compute Hubble rate at t0
112    rhoK = 0.5*dphi**2
113    rhoV = V(phi)
114    H0 = friedmann( rhoK, rhoV, rhoChi )
115    
116    omega = H0 #Characteristic frequency is the initial Hubble rate
117    mu = 1. #Charatcterisic amplitude is the Planck mass
118    
119    return omega, mu

Define how initial data is used to set the reference frequency, $\omega$ and energy scale, $\mu$.

  • energy scale: $M_{\rm P}$ in Planck units
  • frequency scale: initial Hubble rate, $H_0$ (in Planck units)
def initial_conditions(sys, ntr):
122def initial_conditions(sys, ntr):
123    yini = np.zeros((ntr+1)*3+6)
124
125    #from the 'input' dictionary
126    yini[0] = sys.N.value
127    yini[1] = sys.phi.value
128    yini[2] = sys.dphi.value
129
130    #needs to be computed
131    sys.initialise("kh")( abs(sys.dphi)*sys.beta )
132    yini[3] = np.log(sys.kh.value)
133
134    #initialise delta and rhoChi
135    yini[4] = sys.delta.value
136    yini[5] = sys.rhoChi.value
137
138    #all gauge-field expectation values are assumed to be 0 at initialisation
139    return yini

Define how to create an array of initial data to solve the ODE's.

Parameters
  • vals (BGSystem): contains initial data
  • ntr (int): truncation number
Returns
  • yini (NDArray): initial data in an array
def update_values(t, y, sys, atol=1e-20, rtol=1e-06):
142def update_values(t, y, sys, atol=1e-20, rtol=1e-6):
143    #spacetime variables
144    sys.t.value = t
145    sys.N.value = y[0]
146    sys.a.value = np.exp(y[0])
147
148    #parse for convenience
149    sys.phi.value = y[1]
150    sys.dphi.value = y[2]
151    sys.kh.value = np.exp(y[3])
152    sys.delta.value = y[4]
153    sys.rhoChi.value = y[5]
154
155    #the gauge-field terms in y are not stored, save these values here
156    sys.E.value = y[6]*np.exp(4*(y[3]-y[0]))
157    sys.B.value = y[7]*np.exp(4*(y[3]-y[0]))
158    sys.G.value = y[8]*np.exp(4*(y[3]-y[0]))
159
160    #Hubble rate
161    sys.H.value = ( friedmann(0.5*sys.dphi**2, sys.V(sys.phi), 
162                                0.5*(sys.E+sys.B)*sys.omega**2, sys.rhoChi*sys.omega**2) )
163
164    #conductivities
165    sigmaE, sigmaB, ks = conductivity(sys.a.value, sys.H.value, sys.E.value,
166                                       sys.B.value, sys.G.value, sys.omega)
167
168    GlobalFerm = heaviside(np.log(ks),np.log(sys.a*sys.H))
169    sys.sigmaE.value = (GlobalFerm*sigmaE)
170    sys.sigmaB.value = (GlobalFerm*sigmaB)
171
172    #boundary term parameters
173    sys.s.value = sys.sigmaE/(2*sys.H)
174    sys.xi.value = sys.beta*(sys.dphi/(2*sys.H))
175    sys.xieff.value = sys.xi + sys.sigmaB/(2*sys.H)
176
177    #acceleration for convenience
178    sys.ddphi.value = klein_gordon(sys.dphi, sys.dV(sys.phi), sys.H, -sys.G*sys.beta*sys.omega**2)
179    return

Compute static variables.

Parameters
  • t (float): time
  • y (float): array of data
  • vals (BGSystem): system of data storing static variables
  • atol, rtol (float): precisions (used for heaviside functions)
def compute_timestep(t, y, sys, atol=1e-20, rtol=1e-06):
181def compute_timestep(t, y, sys, atol=1e-20, rtol=1e-6):
182    dydt = np.zeros(y.shape)
183
184    #odes for N and phi
185    dydt[0] = sys.H.value
186    dydt[1] = sys.dphi.value
187    dydt[2] = sys.ddphi.value
188
189    #achieving dlnkhdt is monotonous requires some care
190    dlnkhdt = dlnkh_schwinger( sys.kh, sys.dphi, sys.ddphi, sys.beta,
191                                        0., sys.xieff, sys.s, sys.a, sys.H )
192    xieff = sys.xieff.value
193    s = sys.s.value
194    sqrtterm = np.sqrt(xieff**2 + s**2 + s)
195    r = (abs(xieff) + sqrtterm)
196    logfc = y[0] + np.log(r*dydt[0]) 
197    dlnkhdt *= heaviside(dlnkhdt,0)*heaviside(logfc,y[3]*(1-1e-5))
198    dydt[3] = dlnkhdt
199
200    #the other derivatives are straight forwards
201    dydt[4] = ddelta( sys.delta, sys.sigmaE )
202    dydt[5] = drhoChi( sys.rhoChi, sys.E, sys.G, sys.sigmaE, sys.sigmaB, sys.H )
203
204    #compute boundary terms and then the gauge-field bilinear ODEs
205    Fcol = y[6:].shape[0]//3
206    F = y[6:].reshape(Fcol,3)
207    W = boundary_fai(sys.xieff.value, sys.s.value)
208    dFdt = gauge_field_ode_schwinger( F, sys.a, sys.kh, 2*sys.H*sys.xieff,
209                                            sys.sigmaE, sys.delta,
210                                                W, dlnkhdt )
211    #reshape to fit dydt
212    dydt[6:] = dFdt.reshape(Fcol*3)
213
214    return dydt

Compute time derivatives for dynamical variables.

Parameters
  • t (float): time
  • y (float): array of data
  • vals (BGSystem): system of data storing static variables
  • atol, rtol (float): precisions (used for heaviside functions)
Returns
  • dydt (NDArray): time derivative of y
def condition_EndOfInflation(t, y, sys):
217def condition_EndOfInflation(t, y, sys):
218   #compute energy densities
219    rhoV = sys.V(y[1])
220    rhoK = y[2]**2/2
221    rhoEM = 0.5*(y[6]+y[7])*(sys.omega/sys.mu)**2*np.exp(4*(y[3]-y[0]))
222    rhoF = y[5]*(sys.omega/sys.mu)**2
223
224    #compute pressure
225    pV = -rhoV
226    pK = rhoK
227    pEM = 1/3*rhoEM
228    pF = 1/3*rhoF
229
230    return check_accelerated_expansion([rhoV, rhoK, rhoEM, rhoF], [pV, pK, pEM, pF])/(sys.H)**2

Determine the end of inflation using $\ddot{a} = 0$

Parameters
  • t (float): time
  • y (float): array of data
  • vals (BGSystem): system of data storing static variables
def consequence_EndOfInflation(sys, occurance):
232def consequence_EndOfInflation(sys, occurance):
233    if occurance:
234        #stop solving once the end of inflation is reached
235        return "finish", {}
236    else:
237        #increase tend given the current Hubble rate
238        tdiff = np.round(5/sys.H, 0)
239        #round again, sometimes floats cause problems in t_span and t_eval.
240        tend  = np.round(sys.t + tdiff, 0)
241        return "proceed", {"tend":tend}

Define how the solver response to a (non)-occurrence of the end of inflation.

If the event occurs, the solultion is accepted. Else, increase $t_{\rm end}$ and continue solving.

Parameters
  • vals (BGSystem): solution to an ODE
  • occurrence (bool): if the event has occurred during the ODE solution
EndOfInflation = <geff.solver.TerminalEvent object>

Defines the 'End of inflation' event.

Defines the 'Negative norms' event.

solver = <class 'geff.solver.GEFSolver'>

The solver used by the GEF model.

MbM = <class 'geff.mbm.ModeSolver'>

The mode solver used by the GEF model.