Пример #1
0
def get_tight_coupling_index(x, k, rec_info, bg_params, x_end_rec):
    """ Here, x and k are assumed to be an n_k * n_x grid of values """

    n_k = x.shape[0]
    n_x = x.shape[1]
    #We only evaluate the first column of the x grid and copy the result to
    #the other columns since it is independent of k
    tau_p = np.zeros(x.shape)
    tau_p[0] = rec_info.get_tau_primed(x[0])
    tau_p[1:] = np.array((n_k - 1) * [tau_p[0]])
    #This is evaluated on a n_x * n_k grid
    ck_byH_ptau_p = s_o_l * k / (tau_p * time_mod.get_H_p(x, bg_params))
    #Again, we find the index just for the first tau array.
    ind = np.searchsorted(np.abs(tau_p[0])[::-1], 10)
    ind = n_x - ind
    #This also is independent of k, so we do this step first
    if x_end_rec < x[0, ind]:
        ind = np.searchsorted(x[0, :], x_end_rec)
    #Here, there is a k dependence so we make a list comprehension that loops
    #through all ks
    ind = np.array([ind if ck_byH_ptau_p[i, ind] <= 0.1 else np.searchsorted(ck_byH_p_tau_p[i, :], 0.1) for i in xrange(n_k)])
    #Ind should now be an n_k array
    return ind
Пример #2
0
    def __init__(self,
                 bg_params,
                 a_start=None,
                 a_end=None,
                 a_npoints=None,
                 x_array=None,
                 tau_init=0):
        if x_array is None:
            self.x_array = np.linspace(np.log(a_start), np.log(a_end), a_npoints)
        else:
            self.x_array = x_array
        self.a_array = np.exp(self.x_array)
        self.bg_params = bg_params
        if tau_init != 0:
            raise NotImplementedError('tau_init must currently be zero')
        self.tau_init = tau_init

        #Precompute info for splining
        self.ne_array = compute_ne(self.x_array, self.bg_params)
        self.tau_der_array = -self.ne_array * s_o_l * sigma_T * np.exp(self.x_array) / time_mod.get_H_p(self.x_array, self.bg_params)
        #Reversing the array because we're starting at today and
        #integrating backwards
        self.tau_array = np.append(np.array([self.tau_init]), cumtrapz(self.tau_der_array[::-1], self.x_array[::-1]))[::-1]
        #Typically, this won't matter, and it avoids log issues
        self.tau_array[-1] = self.tau_array[-2]
        self.g_array = - self.tau_der_array * np.exp(-self.tau_array)

        #Set up splines
        self.ne_spline = interpolation.cubic_spline(self.x_array, np.log(self.ne_array))
        self.tau_spline = interpolation.cubic_spline(self.x_array, np.log(self.tau_array))
        self.tau_der_spline = interpolation.cubic_spline(self.x_array, np.log(-self.tau_der_array))

        self.g_spline = interpolation.cubic_spline(self.x_array, self.g_array)
        #Spline the second derivative of the log of g
        self.g2_spline = interpolation.cubic_spline(self.x_array, self.g_spline)
Пример #3
0
def evolve_boltzmann(x, k, bg_params, rec_info, eta_info, lmax, lmax_nu, x_end_rec):
    """ x and k should be n_k * n_x grids """
    n_k = x.shape[0]
    n_x = x.shape[1]
    ind = get_tight_coupling_index(x, k, rec_info, bg_params, x_end_rec)
    #Don't necessarily have to do this for every x, could evaluate for one k
    #and copy, but this step is only done once, so very little to save here
    divH_p = 1 / time_mod.get_H_p(x, bg_params)
    divtau_p = 1 / rec_info.get_tau_primed(x)
    ck = s_o_l * k
    a = np.exp(x)
    H_0 = bg_params.H_0
    omega_nu = bg_params.omega_nu
    omega_r = bg_params.omega_r
    divf_nu = (omega_nu + omega_r) / omega_nu

    theta = np.zeros((lmax + 1, n_k, n_x))
    theta_p = np.zeros((lmax + 1, n_k, n_x))
    n = np.zeros((lmax_nu + 1, n_k, n_x))
    #Set up initial conditions
    phi = np.ones((n_k, n_x))
    delta = 1.5 * phi
    delta_b = delta
    v = 0.5 * ck * divH_p * phi
    v_b = v
    theta[0] = 0.5 * phi
    theta[1] = -ck * divH_p * phi / 6
    n[0] = 0.5 * phi
    n[1] = -ck * divH_p / 6 * phi
    n[2] = - ck ** 2 * a ** 2 * phi / (12 * H_0 ** 2 * omega_nu * (2.5 * divf_nu + 1))
    for l in xrange(3, lmax_nu + 1):
        n[l] = ck * divH_p / (2 * l + 1) * n[l-1]

    #Just some handy indices (valid after tight coupling)
    theta_p_start = lmax + 3
    n_start = 2 * lmax + 4
    n_end = 2 * lmax + lmax_nu + 5
#    print ind
#    print k[:, 0]
#    sys.exit()

    #Solve the actual equations
    #TODO - could this be done without the for loop?
    for k_i in xrange(n_k):
        print k_i
        #if k_i == 5:
        #    sys.exit()
        #First evolve until tight coupling is no longer valid
        x_tight_coupling = x[k_i, :ind[k_i]]
        y0 = np.array([])
        #This can also be done better, I am sure
        for el in (phi[k_i, 0], v_b[k_i, 0], theta[:2, k_i, 0], n[:, k_i, 0], delta_b[k_i, 0], v[k_i, 0], delta[k_i, 0]):
            y0 = np.append(y0, el)
        #Do the integration
        print 'tight_coupling'
        out = ode_mod.Output(nsave=-1, force_stepsize=True, xarr=x_tight_coupling)
        tc_solver = ode_mod.OdeInt(y0, x_tight_coupling[0], x_tight_coupling[-1], atol=1e-3, rtol=1e-3, h1=0.01, hmin=0.0, out=out, derivs=einstein_boltzmann_tight_coupling(k[k_i, 0], rec_info, eta_info, lmax, lmax_nu, bg_params))
        tc_solver.integrate()
        res = tc_solver.out.ysave
        print 'nok', tc_solver.nok
        print 'nbad', tc_solver.nbad

        phi[k_i, 0:ind[k_i]] = res[0, :]
        v_b[k_i, 0:ind[k_i]] = res[1, :]
        theta[:2, k_i, 0:ind[k_i]] = res[2:4, :]
        n[:, k_i, 0:ind[k_i]] = res[4:lmax_nu + 5, :]
        delta_b[k_i, 0:ind[k_i]] = res[lmax_nu + 5, :]
        v[k_i, 0:ind[k_i]] = res[lmax_nu + 6, :]
        delta[k_i, 0:ind[k_i]] = res[lmax_nu + 7, :]

        #Set up the derived quantities during tight coupling. Alternative here
        #is to wait to after the loop, but since ind will be k-dependent, this
        #means copying more than strictly necessary
        theta[2, k_i, 0:ind[k_i]] = -8 * ck[k_i, 0:ind[k_i]] * divH_p[k_i, 0:ind[k_i]] * divtau_p[k_i, 0:ind[k_i]] / 15 * theta[1, k_i, 0:ind[k_i]]
        for l in xrange(3, lmax + 1):
            theta[l, k_i, 0:ind[k_i]] = - l * ck[k_i, 0:ind[k_i]] / (2 * l + 1) * divH_p[k_i, 0:ind[k_i]] * divtau_p[k_i, 0:ind[k_i]] * theta[l-1, k_i, 0:ind[k_i]]
        theta_p[0, k_i, 0:ind[k_i]] = 1.25 * theta[2, k_i, 0:ind[k_i]]
        theta_p[1, k_i, 0:ind[k_i]] = -0.25 * ck[k_i, 0:ind[k_i]] * divH_p[k_i, 0:ind[k_i]] * divtau_p[k_i, 0:ind[k_i]] * theta[2, k_i, 0:ind[k_i]]
        theta_p[2, k_i, 0:ind[k_i]] = 0.25 * theta[2, k_i, 0:ind[k_i]]
        for l in xrange(3, lmax + 1):
            theta_p[l, k_i, 0:ind[k_i]] = - l / (2 * l + 1) * ck[k_i, 0:ind[k_i]] * divH_p[k_i, 0:ind[k_i]] * divtau_p[k_i, 0:ind[k_i]] * theta_p[l-1, k_i, 0:ind[k_i]]

        #Now evolve after tight coupling
        x_after_tc = x[k_i, ind[k_i] - 1:]
        y0 = np.array([])
        #Use last step of tight coupling as initial conditions for current step
        for el in (phi[k_i, ind[k_i]-1], v_b[k_i, ind[k_i]-1], theta[:,k_i, ind[k_i]-1], theta_p[:,k_i, ind[k_i]-1], n[:,k_i, ind[k_i]-1], delta_b[k_i, ind[k_i]-1], v[k_i, ind[k_i]-1], delta[k_i, ind[k_i]-1]):
            y0 = np.append(y0, el)
        print 'post'
        print 'y0', y0
        out = ode_mod.Output(nsave=-1, force_stepsize=True, xarr=x_after_tc)
        tc_solver = ode_mod.OdeInt(y0, x_after_tc[0], x_after_tc[-1], atol=1e-3, rtol=1e-3, h1=0.01, hmin=0.0, out=out, derivs=einstein_boltzmann(k[k_i, 0], rec_info, eta_info, lmax, lmax_nu, bg_params), stepper='sie')
        tc_solver.integrate()
        res = tc_solver.out.ysave
        print 'nok', tc_solver.nok
        print 'nbad', tc_solver.nbad


        phi[k_i, ind[k_i]-1:] = res[0, :]
        v_b[k_i, ind[k_i]-1:] = res[1, :]
        theta[:,k_i, ind[k_i]-1:] = res[2:theta_p_start, :]
        theta_p[:, k_i, ind[k_i] - 1:] = res[theta_p_start:n_start, :]
        n[:, k_i, ind[k_i] - 1:] = res[n_start:n_end, :]
        delta_b[k_i, ind[k_i] - 1:] = res[n_end, :]
        v[k_i, ind[k_i] - 1:] = res[n_end + 1, :]
        delta[k_i, ind[k_i] - 1:] = res[n_end + 2, :]
        np.savetxt('k%02d_phi.dat' % k_i, phi[k_i, :])
        np.savetxt('k%02d_v_b.dat' % k_i, v_b[k_i, :])
        for l in range(lmax + 1):
            np.savetxt('k%02d_theta%02d.dat' % (k_i, l), theta[l, k_i])
            np.savetxt('k%02d_theta_p%02d.dat' % (k_i, l), theta_p[l, k_i])
        for l in range(lmax_nu + 1):
            np.savetxt('k%02d_n%02d.dat' % (k_i, l), n[l, k_i])
        np.savetxt('k%02d_delta_b.dat' % k_i, delta_b[k_i, :])
        np.savetxt('k%02d_v.dat' % k_i, v[k_i, :])
        np.savetxt('k%02d_delta.dat' % k_i, delta[k_i, :])

        np.savetxt('k%02d_ckdivH_p.dat' % k_i, (ck * divH_p)[k_i, :])
        np.savetxt('k%02d_12H0divk2a2.dat' % k_i, (12 * H_0 ** 2 / ck ** 2 / a ** 2)[k_i, :])
        np.savetxt('k%02d_psi.dat' % k_i, (-phi - 12 * H_0 ** 2 / ck ** 2 / a ** 2 * (omega_r * theta[2] + omega_nu * n[2]))[k_i])

    return (phi, v_b, theta, theta_p, n, delta_b, v, delta)
Пример #4
0
 def jacobian(self, x, y):
     diva = np.exp(-x)
     omega_r = self.bg_params.omega_r
     omega_b = self.bg_params.omega_b
     omega_nu = self.bg_params.omega_nu
     omega_m = self.bg_params.omega_m
     tau_p = self.rec_info.get_tau_primed(x)
     divH_p = 1 / time_mod.get_H_p(x, self.bg_params)
     l_array = self.l_array
     delta_l_2 = self.delta_l_2
     diveta = 1 / self.eta_info.get_eta(x)
     div3 = self.div3
     ck = self.ck
     ckdivH_p = ck * divH_p
     divck = self.divck
     H_0 = self.bg_params.H_0
     lmax = self.lmax
     lmax_nu = self.lmax_nu
 
     theta_start = 2
     theta_p_start = self.theta_p_start
     n_start = self.n_start
     n_end = self.n_end
 
     R = 4 * omega_r * div3 * diva / omega_b
     numvars = len(y)
     dpi = np.zeros(numvars)
     dpi[theta_start + 2] = 1
     dpi[theta_p_start] = 1
     dpi[theta_p_start+2] = 1
     dpsi = np.zeros(numvars)
     dpsi[0] = -1
     dpsi[theta_start + 2] = -12 * H_0 ** 2 * diva ** 2 * divck ** 2 * omega_r
     dpsi[n_start + 2] = -12 * H_0 ** 2 * diva ** 2 * divck ** 2 * omega_nu
     res = np.zeros((numvars, numvars))
     #Dtheta
     res[0, 0] = - ckdivH_p ** 2 * div3
     res[0, theta_start] = 2 * (H_0 * divH_p * diva) ** 2 * omega_r
     res[0, n_start] = 2 * (H_0 * divH_p * diva) ** 2 * omega_nu
     res[0, n_end] = 0.5 * diva * (H_0 * divH_p) ** 2 * omega_m
     res[0, n_end + 2] = 0.5 * diva * (H_0 * divH_p) ** 2 * omega_b
     res[0, :] += dpsi
     #Dv_b
     res[1, 1] = -1 + tau_p * R
     res[1, theta_start + 1] = 3 * tau_p * R
     res[1, :] -= ckdivH_p * dpsi
     #Dtheta0
     res[theta_start, theta_start + 1] = -ckdivH_p
     res[theta_start, :] -= res[0, :]
     #Dtheta1
     res[theta_start + 1, 1] = tau_p * div3
     res[theta_start + 1, theta_start] = ckdivH_p * div3
     res[theta_start + 1, theta_start + 1] = tau_p
     res[theta_start + 1, theta_start + 2] = -2 * ckdivH_p * div3
     res[theta_start + 1, :] -= ckdivH_p * div3 * dpsi
     #Dtheta2
     res[theta_start + 2, theta_start + 1] = 0.4 * ckdivH_p
     res[theta_start + 2, theta_start + 2] = tau_p
     res[theta_start + 2, theta_start + 3] = - 0.6 * ckdivH_p
     res[theta_start + 2, :] -= 0.1 * tau_p * dpi
     #Dthetal
     base_array = np.array([l_array * ckdivH_p / (2 * l_array + 1), [tau_p] * len(l_array), - (l_array + 1) * ckdivH_p / (2 * l_array + 1)]).T
     base_matrix = np.array([[base_array[l, i-l+1] if abs(l-i) < 2 else 0 for i in xrange(2, lmax + 1)] for l in xrange(3, lmax)])
     res[theta_start + 3:theta_p_start - 1, theta_start + 2:theta_p_start] = base_matrix
     #Dthetalmax
     res[theta_p_start-1, theta_p_start - 2] = -ckdivH_p
     res[theta_p_start - 1, theta_p_start - 1] = -(lmax + 1) * divH_p * diveta + tau_p
     #Dtheta_p0
     res[theta_p_start, theta_p_start] = tau_p
     res[theta_p_start, theta_p_start + 1] = -ckdivH_p
     res[theta_p_start, :] -= 0.5 * tau_p * dpi
     #Dtheta_pl
     base_matrix = np.array([[base_array[l, i-l+1] if abs(l-i) < 2 else 0 for i in xrange(0, lmax+1)] for l in xrange(1, lmax)])
     res[theta_p_start + 1:n_start - 1, theta_p_start:n_start] = base_matrix
     #For theta2 we have the pi contribution
     res[theta_p_start + 2, :] -= 0.1 * tau_p * dpi
     #Dtheta_plmax
     res[n_start - 1, n_start - 2] = ckdivH_p
     res[n_start - 1, n_start - 1] = - (lmax + 1) * divH_p * diveta + tau_p
     #N0
     res[n_start, n_start + 1] = -ckdivH_p
     res[n_start, :] -= res[0, :]
     #N1
     res[n_start + 1, n_start] = ckdivH_p * div3
     res[n_start + 1, n_start + 2] = -2 * ckdivH_p * div3
     res[n_start + 1, :] += ckdivH_p * div3 * dpsi
     #Nl
     base_array = np.array([l_array * ckdivH_p / (2 * l_array + 1), np.zeros(len(l_array)), - (l_array + 1) * ckdivH_p / (2 * l_array + 1)]).T
     base_matrix = np.array([[base_array[l, i-l+1] if abs(l-i) == 1 else 0 for i in xrange(1, lmax_nu + 1)] for l in xrange(2, lmax_nu)])
     res[n_start + 2:n_end - 1, n_start + 1:n_end] = base_matrix
     #Nlmax
     res[n_end-1, n_end-2] = ckdivH_p
     res[n_end-1, n_end-1] = - (lmax_nu + 1) * divH_p * diveta
     #delta_b
     res[n_end, 1] = ckdivH_p
     res[n_end, :] -= 3 * res[0, :]
     #v
     res[n_end + 1, n_end + 1] = -1
     res[n_end + 1, :] -= ckdivH_p * dpsi
     #delta
     res[n_end + 2, n_end + 1] = ckdivH_p
     res[n_end + 2, :] -= 3 * res[0, :]
 
     return res
Пример #5
0
    def __call__(self, x, y):
        """ Returns dy/dx, where y is the vector of all the Einstein-Boltzmann equations """

        #Set up useful aliases
        res = np.zeros(len(y))
        diva = np.exp(-x)
        omega_r = self.bg_params.omega_r
        omega_b = self.bg_params.omega_b
        omega_nu = self.bg_params.omega_nu
        omega_m = self.bg_params.omega_m
        #print x
        tau_p = self.rec_info.get_tau_primed(x)
        divH_p = 1 / time_mod.get_H_p(x, self.bg_params)
        l_array = self.l_array
        delta_l_2 = self.delta_l_2
        diveta = 1 / self.eta_info.get_eta(x)
        div3 = self.div3
        ck = self.ck
        H_0 = self.bg_params.H_0
        lmax = self.lmax
        lmax_nu = self.lmax_nu

        theta_p_start = self.theta_p_start
        n_start = self.n_start
        n_end = self.n_end

        phi = y[0]
        v_b = y[1]
        theta = y[2:theta_p_start]
        theta_p = y[theta_p_start:n_start]
        n = y[n_start:n_end]
        delta_b = y[n_end]
        v = y[n_end + 1]
        delta = y[n_end + 2]

        #Begin calculations
        R = 4 * omega_r * div3 * diva / omega_b
        PI = theta[2] + theta_p[0] + theta_p[2]
        psi = -phi - 12 * H_0 ** 2 * (omega_r * theta[2] + omega_nu * n[2]) * diva ** 2 / ck ** 2
        #phi
        res[0] = psi - ck ** 2 * phi * divH_p ** 2 * div3 + H_0 ** 2 * 0.5 * divH_p ** 2 * (omega_m * delta * diva + omega_b * diva * delta_b + 4 * omega_r * diva ** 2 * theta[0] + 4 * omega_nu * diva ** 2 * n[0])
        #vb
        res[1] = -v_b - ck * divH_p * psi + tau_p * R * (3 * theta[1] + v_b)
#        print 'res', res[1]
#        print 'vb', v_b
#        print 'ck', ck
#        print 'divH_p', divH_p
#        print 'psi', psi
#        print 'tau_p', tau_p
#        print 'R', R
#        print 'threethetaplusvb', 3 * theta[1] + v_b
#        print 'theta1', theta[1]
        #theta0
        res[2] = -ck * divH_p * theta[1] - res[0]
        #theta1
        res[3] = ck * div3 * divH_p * theta[0] - 2 * ck * div3 * divH_p * theta[2] + ck * div3 * divH_p * psi + tau_p * (theta[1] + div3 * v_b)
#        print 'theta0', theta[0]
#        print 'theta2', theta[2]
#        print 'theta1plusonethirdvb', theta[1] + div3 * v_b
#        print 'dphi', res[0]
#        print 'ckdivHtheta1', ck*divH_p * theta[1]
        #theta2-lmax-1
        res[4:theta_p_start-1] = ck * divH_p * l_array[2:lmax] / (2 * l_array[2:lmax] + 1) * theta[1:lmax-1] - (l_array[2:lmax] + 1) * ck * divH_p / (2 * l_array[2:lmax] + 1) * theta[3:lmax+1] + tau_p * (theta[2:lmax] - 0.1 * PI * delta_l_2[2:lmax])
#        print 'dtheta2', res[4]
#        print 'theta3', theta[3]
#        print 'thetap2', theta_p[2]
#        print 'thetap0', theta_p[0]
#        print 'pi', PI
        #thetalmax
        res[theta_p_start - 1] = ck * divH_p * theta[lmax - 1] - (lmax + 1) * divH_p * diveta * theta[lmax] + tau_p * theta[lmax]
        #Theta_p
        res[theta_p_start] = -ck * divH_p * theta_p[1] + tau_p * (theta_p[0] - 0.5 * PI)
        res[theta_p_start+1:n_start-1] = ck * divH_p * l_array[1:lmax] / (2 * l_array[1:lmax] + 1) * theta_p[0:lmax-1] - (l_array[1:lmax] + 1) * ck * divH_p / (2 * l_array[1:lmax] + 1) * theta_p[2:lmax+1] + tau_p * (theta_p[1:lmax] - 0.1 * PI * delta_l_2[1:lmax])
        res[n_start - 1] = ck * divH_p * theta_p[lmax - 1] - (lmax + 1) * divH_p * diveta * theta_p[lmax] + tau_p * theta_p[lmax]

        #Neutrino multipoles
        res[n_start] = -ck * divH_p * n[1] - res[0]
        res[n_start + 1] = ck * divH_p * div3 * n[0] - 2 * ck * divH_p * div3 * n[2] + ck * divH_p * div3 * psi
        res[n_start+2:n_end-1] = ck * divH_p * l_array[2:lmax_nu] / (2 * l_array[2:lmax_nu] + 1) * n[1:lmax_nu-1] - (l_array[2:lmax_nu] + 1) * ck * divH_p * (2 * l_array[2:lmax_nu] + 1) * n[3:lmax_nu + 1]
        res[n_end - 1] = ck * divH_p * n[lmax_nu-1] - (lmax_nu + 1) * n[lmax_nu] * divH_p * diveta
        #db, v, d
        res[n_end] = ck * divH_p * v_b - 3 * res[0]
        res[n_end + 1] = -v - ck * divH_p * psi
        res[n_end + 2] = ck * divH_p * v - 3 * res[0]

        return res
Пример #6
0
    def __call__(self, x, y):
        """ Returns dy/dx, where y is the vector of all the Einstein-Boltzmann equations, during tight coupling """
        #Set up useful aliases
        res = np.zeros(len(y))
        diva = np.exp(-x)
        tau_p = self.rec_info.get_tau_primed(x)
        tau_dp = self.rec_info.get_tau_double_primed(x)
        divH_p = 1 / time_mod.get_H_p(x, self.bg_params)
        dH_p = time_mod.get_dH_p(x, self.bg_params)
        diveta = 1 / self.eta_info.get_eta(x)
        n_start = self.n_start
        n_end = self.n_end
        lmax = self.lmax
        ck = self.ck
        lmax_nu = self.lmax_nu
        k = self.k
        H_0 = self.H_0
        div3 = self.div3
        delta_l_2 = self.delta_l_2
        l_array = self.l_array
        omega_r = self.omega_r
        omega_b = self.omega_b
        omega_nu = self.omega_nu
        omega_m = self.omega_m

        phi = y[0]
        v_b = y[1]
        theta = y[2:n_start]
        n = y[n_start:n_end]
        delta_b = y[n_end]
        v = y[n_end + 1]
        delta = y[n_end + 2]
        theta_2 = -8 * ck * divH_p / (15 * tau_p) * theta[1]

        #Start calculations
        R = 4 * omega_r * div3 * diva / omega_b
        psi = -phi - 12 * H_0 ** 2 * (omega_r * theta_2 + omega_nu * n[2]) * diva ** 2 / ck ** 2
    
        #Phi
        res[0] = psi - ck ** 2 * phi * divH_p ** 2 * div3 + H_0 ** 2 * 0.5 * divH_p ** 2 * (omega_m * delta * diva + omega_b * diva * delta_b + 4 * omega_r * diva ** 2 * theta[0] + 4 * omega_nu * diva ** 2 * n[0])
        #Theta0
        res[2] = -ck * divH_p * theta[1] - res[0]
    
        q = (-((1 - 2*R) * tau_p + (1 + R) * tau_dp) * (3 * theta[1] + v_b) - ck * divH_p * psi + (1 - dH_p * divH_p) * ck * divH_p * (-theta[0] + 2 * theta_2) - ck * divH_p * res[2]) / ((1 + R) * tau_p + dH_p * divH_p - 1)
        #vb
        res[1] = (-v_b - ck * divH_p * psi + R * (q + ck * divH_p*(-theta[0] + 2 * theta_2) - ck * divH_p * psi)) / (1 + R)
    #    print 'vb', res[1]
        #Theta1
        res[3] = div3 * (q - res[1])
        #Neutrino multipoles
        res[n_start] = -ck * divH_p * n[1] - res[0]
        res[n_start + 1] = ck * divH_p * div3 * n[0] - 2 * ck * divH_p * div3 * n[2] + ck * divH_p * div3 * psi
        res[n_start+2:n_end-1] = ck * divH_p * l_array[2:lmax_nu] / (2 * l_array[2:lmax_nu] + 1) * n[1:lmax_nu-1] - (l_array[2:lmax_nu] + 1) * ck * divH_p * (2 * l_array[2:lmax_nu] + 1) * n[3:lmax_nu + 1]
        res[n_end - 1] = ck * divH_p * n[lmax_nu-1] - (lmax_nu + 1) * n[lmax_nu] * divH_p * diveta
        #db, v, d
        res[n_end] = ck * divH_p * v_b - 3 * res[0]
        res[n_end + 1] = -v - ck * divH_p * psi
        res[n_end + 2] = ck * divH_p * v - 3 * res[0]
    #    print 'v', res[n_end + 1]
    
        return res