Пример #1
0
def clkk_gen(Omega_m, A_se9, zs=1.0):
    A_s = A_se9 * 1e-9
    omch2 = (OmegaM - OmegaB) * h**2
    LambdaCDM = Class()
    LambdaCDM.set({
        'omega_b': ombh2,
        'omega_cdm': omch2,
        'h': h,
        'A_s': A_s,
        'n_s': n_s
    })
    LambdaCDM.set({
        'output': 'mPk,sCl',
        'P_k_max_1/Mpc': 10.0,
        'l_switch_limber': 100,
        'selection': 'dirac',
        'selection_mean': zs,
        'l_max_lss': lmax,
        'non linear': 'halofit'
    })
    # run class
    LambdaCDM.compute()

    si8 = LambdaCDM.sigma8()

    cls = LambdaCDM.density_cl(lmax)
    ell = cls['ell'][2:]
    clphiphi = cls['ll'][0][2:]
    clkk = 1.0 / 4 * (ell + 2.0) * (ell + 1.0) * (ell) * (ell - 1.0) * clphiphi

    return si8, ell, clkk
Пример #2
0
class TestClass(unittest.TestCase):
    """
    Testing Class and its wrapper classy on different cosmologies

    To run it, do
    ~] nosetest test_class.py

    It will run many times Class, on different cosmological scenarios, and
    everytime testing for different output possibilities (none asked, only mPk,
    etc..)

    """
    @classmethod
    def setUpClass(cls):
        cls.faulty_figs_path = os.path.join(
            os.path.sep.join(
                os.path.realpath(__file__).split(os.path.sep)[:-1]),
            'faulty_figs')

        if os.path.isdir(cls.faulty_figs_path):
            shutil.rmtree(cls.faulty_figs_path)

        os.mkdir(cls.faulty_figs_path)

    @classmethod
    def tearDownClass(cls):
        pass

    def setUp(self):
        """
        set up data used in the tests.
        setUp is called before each test function execution.
        """
        self.cosmo = Class()
        self.cosmo_newt = Class()

        if CLASS_VERBOSE:
            self.verbose = {
                'input_verbose': 1,
                'background_verbose': 1,
                'thermodynamics_verbose': 1,
                'perturbations_verbose': 1,
                'transfer_verbose': 1,
                'primordial_verbose': 1,
                'harmonic_verbose': 1,
                'fourier_verbose': 1,
                'lensing_verbose': 1,
                'distortions_verbose': 1,
                'output_verbose': 1,
            }
        else:
            self.verbose = {}
        self.scenario = {}

    def tearDown(self):
        self.cosmo.struct_cleanup()
        self.cosmo.empty()
        self.cosmo = 0
        self.cosmo_newt.struct_cleanup()
        self.cosmo_newt.empty()
        self.cosmo_newt = 0
        del self.scenario

    def poormansname(self, somedict):
        string = "_".join(
            [k + '=' + str(v) for k, v in list(somedict.items())])
        string = string.replace('/', '%')
        string = string.replace(',', '')
        string = string.replace(' ', '')
        return string

    @parameterized.expand(TUPLE_ARRAY,
                          doc_func=custom_name_func,
                          custom_name_func=custom_name_func)
    @attr('dump_ini_files')
    def test_Valgrind(self, inputdict):
        """Dump files"""
        self.scenario.update(inputdict)
        self.name = self._testMethodName
        if self.has_incompatible_input():
            return
        path = os.path.join(self.faulty_figs_path, self.name)
        self.store_ini_file(path)
        self.scenario.update({'gauge': 'Newtonian'})
        self.store_ini_file(path + 'N')

    @parameterized.expand(TUPLE_ARRAY,
                          doc_func=custom_name_func,
                          custom_name_func=custom_name_func)
    @attr('test_scenario')
    def test_scenario(self, inputdict):
        """Test scenario"""
        self.scenario.update(inputdict)
        self.name = self._testMethodName
        self.cosmo.set(
            dict(itertools.chain(self.verbose.items(), self.scenario.items())))

        cl_dict = {
            'tCl': ['tt'],
            'lCl': ['pp'],
            'pCl': ['ee', 'bb'],
            'nCl': ['dd'],
            'sCl': ['ll'],
        }

        # 'lensing' is always set to yes. Therefore, trying to compute 'tCl' or
        # 'pCl' will fail except if we also ask for 'lCl'.
        if self.has_incompatible_input():
            self.assertRaises(CosmoSevereError, self.cosmo.compute)
            return
        else:
            self.cosmo.compute()

        self.assertTrue(self.cosmo.state,
                        "Class failed to go through all __init__ methods")
        # Depending
        if 'output' in self.scenario.keys():
            # Positive tests of raw cls
            output = self.scenario['output']
            for elem in output.split():
                if elem in cl_dict.keys():
                    for cl_type in cl_dict[elem]:
                        is_density_cl = (elem == 'nCl' or elem == 'sCl')
                        if is_density_cl:
                            cl = self.cosmo.density_cl(100)
                        else:
                            cl = self.cosmo.raw_cl(100)
                        self.assertIsNotNone(cl, "raw_cl returned nothing")
                        cl_length = np.shape(
                            cl[cl_type][0])[0] if is_density_cl else np.shape(
                                cl[cl_type])[0]
                        self.assertEqual(cl_length, 101,
                                         "raw_cl returned wrong size")
                if elem == 'mPk':
                    pk = self.cosmo.pk(0.1, 0)
                    self.assertIsNotNone(pk, "pk returned nothing")
            # Negative tests of output functions
            if not any(
                [elem in list(cl_dict.keys()) for elem in output.split()]):
                # testing absence of any Cl
                self.assertRaises(CosmoSevereError, self.cosmo.raw_cl, 100)
            if 'mPk' not in output.split():
                # testing absence of mPk
                self.assertRaises(CosmoSevereError, self.cosmo.pk, 0.1, 0)

        if COMPARE_OUTPUT_REF or COMPARE_OUTPUT_GAUGE:
            # Now compute same scenario in Newtonian gauge
            self.cosmo_newt.set(
                dict(list(self.verbose.items()) + list(self.scenario.items())))
            self.cosmo_newt.set({'gauge': 'newtonian'})
            self.cosmo_newt.compute()

        if COMPARE_OUTPUT_GAUGE:
            # Compare synchronous and Newtonian gauge
            self.assertTrue(
                self.cosmo_newt.state,
                "Class failed to go through all __init__ methods in Newtonian gauge"
            )

            self.compare_output(self.cosmo, "Synchronous", self.cosmo_newt,
                                'Newtonian', COMPARE_CL_RELATIVE_ERROR_GAUGE,
                                COMPARE_PK_RELATIVE_ERROR_GAUGE)

        if COMPARE_OUTPUT_REF:
            # Compute reference models in both gauges and compare
            cosmo_ref = classyref.Class()
            cosmo_ref.set(self.cosmo.pars)
            cosmo_ref.compute()
            status = self.compare_output(cosmo_ref, "Reference", self.cosmo,
                                         'Synchronous',
                                         COMPARE_CL_RELATIVE_ERROR,
                                         COMPARE_PK_RELATIVE_ERROR)
            assert status, 'Reference comparison failed in Synchronous gauge!'

            cosmo_ref = classyref.Class()
            cosmo_ref.set(self.cosmo_newt.pars)
            cosmo_ref.compute()
            self.compare_output(cosmo_ref, "Reference", self.cosmo_newt,
                                'Newtonian', COMPARE_CL_RELATIVE_ERROR,
                                COMPARE_PK_RELATIVE_ERROR)
            assert status, 'Reference comparison failed in Newtonian gauge!'

    def has_incompatible_input(self):

        should_fail = False

        # If we have tensor modes, we must have one tensor observable,
        # either tCl or pCl.
        if has_tensor(self.scenario):
            if 'output' not in list(self.scenario.keys()):
                should_fail = True
            else:
                output = self.scenario['output'].split()
                if 'tCl' not in output and 'pCl' not in output:
                    should_fail = True

        # If we have specified lensing, we must have lCl in output,
        # otherwise lensing will not be read (which is an error).
        if 'lensing' in list(self.scenario.keys()):
            if 'output' not in list(self.scenario.keys()):
                should_fail = True
            else:
                output = self.scenario['output'].split()
                if 'lCl' not in output:
                    should_fail = True
                elif 'tCl' not in output and 'pCl' not in output:
                    should_fail = True

        # If we have specified a tensor method, we must have tensors.
        if 'tensor method' in list(self.scenario.keys()):
            if not has_tensor(self.scenario):
                should_fail = True

        # If we have specified non linear, we must have some form of
        # perturbations output.
        if 'non linear' in list(self.scenario.keys()):
            if 'output' not in list(self.scenario.keys()):
                should_fail = True

        # If we ask for Cl's of lensing potential, number counts or cosmic shear, we must have scalar modes.
        # The same applies to density and velocity transfer functions and the matter power spectrum:
        if 'output' in self.scenario and 'modes' in self.scenario and self.scenario[
                'modes'].find('s') == -1:
            requested_output_types = set(self.scenario['output'].split())
            for scalar_output_type in [
                    'lCl', 'nCl', 'dCl', 'sCl', 'mPk', 'dTk', 'mTk', 'vTk'
            ]:
                if scalar_output_type in requested_output_types:
                    should_fail = True
                    break

        # If we specify initial conditions (for scalar modes), we must have
        # perturbations and scalar modes.
        if 'ic' in list(self.scenario.keys()):
            if 'modes' in list(self.scenario.keys()
                               ) and self.scenario['modes'].find('s') == -1:
                should_fail = True
            if 'output' not in list(self.scenario.keys()):
                should_fail = True

        # If we use inflation module, we must have scalar modes,
        # tensor modes, no vector modes and we should only have adiabatic IC:
        if 'P_k_ini type' in list(self.scenario.keys(
        )) and self.scenario['P_k_ini type'].find('inflation') != -1:
            if 'modes' not in list(self.scenario.keys()):
                should_fail = True
            else:
                if self.scenario['modes'].find('s') == -1:
                    should_fail = True
                if self.scenario['modes'].find('v') != -1:
                    should_fail = True
                if self.scenario['modes'].find('t') == -1:
                    should_fail = True
            if 'ic' in list(self.scenario.keys()
                            ) and self.scenario['ic'].find('i') != -1:
                should_fail = True

        return should_fail

    def compare_output(self, reference, reference_name, candidate,
                       candidate_name, rtol_cl, rtol_pk):
        status_pass = True
        for elem in ['raw_cl', 'lensed_cl']:
            # Try to get the elem, but if they were not computed, a
            # CosmoComputeError should be raised. In this case, ignore the
            # whole block.
            try:
                to_test = getattr(candidate, elem)()
            except CosmoSevereError:
                continue
            ref = getattr(reference, elem)()
            for key, value in list(ref.items()):
                if key != 'ell':
                    # For all self spectra, try to compare allclose
                    if key[0] == key[1]:
                        # If it is a 'dd' or 'll', it is a dictionary.
                        if isinstance(value, dict):
                            for subkey in list(value.keys()):
                                try:
                                    np.testing.assert_allclose(
                                        value[subkey],
                                        to_test[key][subkey],
                                        rtol=rtol_cl,
                                        atol=COMPARE_CL_ABSOLUTE_ERROR)
                                except AssertionError:
                                    self.cl_faulty_plot(
                                        elem + "_" + key, value[subkey][2:],
                                        reference_name,
                                        to_test[key][subkey][2:],
                                        candidate_name, rtol_cl)
                                except TypeError:
                                    self.cl_faulty_plot(
                                        elem + "_" + key, value[subkey][2:],
                                        reference_name,
                                        to_test[key][subkey][2:],
                                        candidate_name, rtol_cl)
                        else:
                            try:
                                np.testing.assert_allclose(
                                    value,
                                    to_test[key],
                                    rtol=rtol_cl,
                                    atol=COMPARE_CL_ABSOLUTE_ERROR)
                            except (AssertionError, TypeError) as e:
                                self.cl_faulty_plot(elem + "_" + key,
                                                    value[2:], reference_name,
                                                    to_test[key][2:],
                                                    candidate_name, rtol_cl)
                                status_pass = False
                    # For cross-spectra, as there can be zero-crossing, we
                    # instead compare the difference.
                    else:
                        # First, we multiply each array by the biggest value
                        norm = max(
                            np.abs(value).max(),
                            np.abs(to_test[key]).max())
                        value *= norm
                        to_test[key] *= norm
                        try:
                            np.testing.assert_array_almost_equal(value,
                                                                 to_test[key],
                                                                 decimal=3)
                        except AssertionError:
                            self.cl_faulty_plot(elem + "_" + key, value[2:],
                                                reference_name,
                                                to_test[key][2:],
                                                candidate_name, rtol_cl)
                            status_pass = False

        if 'output' in list(self.scenario.keys()):
            if self.scenario['output'].find('mPk') != -1:
                # testing equality of Pk
                k = np.logspace(-2, log10(self.scenario['P_k_max_1/Mpc']), 50)
                reference_pk = np.array([reference.pk(elem, 0) for elem in k])
                candidate_pk = np.array([candidate.pk(elem, 0) for elem in k])
                try:
                    np.testing.assert_allclose(reference_pk,
                                               candidate_pk,
                                               rtol=rtol_pk,
                                               atol=COMPARE_PK_ABSOLUTE_ERROR)
                except AssertionError:
                    self.pk_faulty_plot(k, reference_pk, reference_name,
                                        candidate_pk, candidate_name, rtol_pk)
                    status_pass = False

        return status_pass

    def store_ini_file(self, path):
        parameters = dict(
            list(self.verbose.items()) + list(self.scenario.items()))
        with open(path + '.ini', 'w') as param_file:
            param_file.write('# ' + str(parameters) + '\n')
            if len(parameters) == 0:
                # CLASS complains if the .ini file does not do anything.
                param_file.write('write warnings = yes\n')
            for key, value in list(parameters.items()):
                param_file.write(key + " = " + str(value) + '\n')

    def cl_faulty_plot(self, cl_type, reference, reference_name, candidate,
                       candidate_name, rtol):
        path = os.path.join(self.faulty_figs_path, self.name)
        fig, axes = plt.subplots(2, 1, sharex=True)
        ell = np.arange(max(np.shape(candidate))) + 2
        factor = ell * (ell + 1) / (2 *
                                    np.pi) if cl_type[-2:] != 'pp' else ell**5
        axes[0].plot(ell, factor * reference, label=reference_name)
        axes[0].plot(ell, factor * candidate, label=candidate_name)
        axes[1].semilogy(ell,
                         100 * abs(candidate / reference - 1),
                         label=cl_type)
        axes[1].axhline(y=100 * rtol, color='k', ls='--')

        axes[-1].set_xlabel(r'$\ell$')
        if cl_type[-2:] == 'pp':
            axes[0].set_ylabel(r'$\ell^5 C_\ell^\mathrm{{{_cl_type}}}$'.format(
                _cl_type=cl_type[-2:].upper()))
        else:
            axes[0].set_ylabel(
                r'$\ell(\ell + 1)/(2\pi)C_\ell^\mathrm{{{_cl_type}}}$'.format(
                    _cl_type=cl_type[-2:].upper()))
        axes[1].set_ylabel('Relative error [%]')

        for ax in axes:
            ax.legend(loc='upper right')

        fig.tight_layout()
        fname = '{}_{}_{}_vs_{}.pdf'.format(path, cl_type, reference_name,
                                            candidate_name)
        fig.savefig(fname, bbox_inches='tight')
        plt.close(fig)

        # Store parameters (contained in self.scenario) to text file
        self.store_ini_file(path)

    def pk_faulty_plot(self, k, reference, reference_name, candidate,
                       candidate_name, rtol):
        path = os.path.join(self.faulty_figs_path, self.name)

        fig, axes = plt.subplots(2, 1, sharex=True)
        axes[0].loglog(k, k**1.5 * reference, label=reference_name)
        axes[0].loglog(k, k**1.5 * candidate, label=candidate_name)
        axes[0].legend(loc='upper right')

        axes[1].loglog(k, 100 * np.abs(candidate / reference - 1))
        axes[1].axhline(y=100 * rtol, color='k', ls='--')

        axes[-1].set_xlabel(r'$k\quad [\mathrm{Mpc}^{-1}]$')
        axes[0].set_ylabel(r'$k^\frac{3}{2}P(k)$')
        axes[1].set_ylabel(r'Relative error [%]')

        fig.tight_layout()
        fname = path + '_pk_{}_vs_{}.pdf'.format(reference_name,
                                                 candidate_name)
        fig.savefig(fname, bbox_inches='tight')
        plt.close(fig)

        # Store parameters (contained in self.scenario) to text file
        self.store_ini_file(path)
Пример #3
0
	fig, axs = plt.subplots(1, 2, figsize=(14, 5))
	finer_z_grid = np.linspace(0, 2, num=2000)
	for i, nz in enumerate(redshiftdistributions):
		ic = str(i+1)
		nz_grid = nz.eval(z_grid)
		finer_nz_grid = nz.eval(finer_z_grid)
		axs[i].plot(finer_z_grid, finer_nz_grid, label='Gaussian Mixture')
		axs[i].plot(z_grid, nz_grid, label='Histogram-ized', ls='steps')
	plt.show()

# Now run Class!
cosmo = Class()
# Scenario 1
cosmo.set(dict(mainparams.items()+scenario1.items()))
cosmo.compute()
cl1 = cosmo.density_cl(mainparams['l_max_lss'])
cosmo.struct_cleanup()
cosmo.empty()
# Scenario 2
cosmo.set(dict(mainparams.items()+scenario2.items()))
cosmo.compute()
cl2 = cosmo.density_cl(mainparams['l_max_lss'])
cosmo.struct_cleanup()
cosmo.empty()

# The Cls should be very close if the histogram is binned finely
nbins = len(redshiftdistributions)
print 'Comparing accuracy of N(z) representation: multigaussian vs histograms'
for i in range(nbins*(nbins+1)/2): 
	err = cl1['dd'][i] / cl2['dd'][i]
	print 'Accuracy of density Cls ', i+1
Пример #4
0
class Model():
    def __init__(self, cosmo=None):
        """
        Initialize the Model class. By default Model uses its own Class
        instance.

        cosmo = external Class instance. Default is None
        """
        if cosmo:
            self.cosmo = cosmo
        else:
            self.cosmo = Class()
        self.computed = {}
        self.texnames = {}

    def __set_scale(self, axes, xscale, yscale):
        """
        Set scales for axes in axes array.

        axes = axes array (e.g. f, ax = plt.subplots(2,2))
        xscale = linear array of xscale.
        yscale = linear array of yscale.

        Scales are set once axes is flatten. Each plot is counted from left to
        right an from top to bottom.
        """
        for i, ax in enumerate(axes.flat):
            ax.set_xscale(xscale[i])
            ax.set_yscale(yscale[i])

    def __set_label(self, axes, xlabel, ylabel):
        """
        Set labels for axes in axes array.

        axes = axes array (e.g. f, ax = plt.subplots(2,2))
        xlabel = linear array of xlabels.
        ylabel = linear array of ylabels.

        Labels are set once axes is flatten. Each plot is counted from left to
        right an from top to bottom.
        """
        for i, ax in enumerate(axes.flat):
            ax.set_xlabel(xlabel[i])
            ax.set_ylabel(ylabel[i])

    def __store_cl(self, cl_dic):
        """
        Store cl's as (l*(l+1)/2pi)*cl, which is much more useful.
        """

        ell = cl_dic['ell'][2:]

        for cl, list_val in cl_dic.iteritems():
            list_val = list_val[2:]
            if (list_val == ell).all():
                cl_dic[cl] = list_val
                continue
            list_val = (ell * (ell + 1) / (2 * np.pi)) * list_val
            cl_dic[cl] = list_val  # Remove first two null items (l=0,1)

        return cl_dic

    def add_derived(self, varied_name, keys, value):
        """
        Add a derived parameter for varied_name dictionary.

        varied_name = varied variable's name.
        keys = list of keys in descending level.
        value = value to store for new dictionary key.
        """

        dic = self.computed[varied_name]

        for key in keys:
            if key not in dic:
                dic[key] = {}

            dic = dic[key]

        dic.update(value)

    def compute_models(self, params, varied_name, index_variable, values,
                       back=[], thermo=[], prim=[], pert=[], trans=[],
                       pk=[0.0001, 0.1, 100], extra=[], update=True,
                       cosmo_msg=False, texname=""):
        """
        Fill dic with the hi_class output structures for the model with given
        params, modifying the varied_name value with values.

        params = parameters to be set in Class. They must be in agreement with
                what is asked for.
        varied_name = the name of the variable you are modifying. It will be
                      used as key in dic assigned to its background structures.
        index_variable = variable's index in parameters_smg array.
        values = varied variable values you want to compute the cosmology for.
        back = list of variables to store from background. If 'all', store the
              whole dictionary.
        thermo = list of variables to store from thermodynamics. If 'all',
                  store the whole dictionary.
        prim = list of variables to store from primordial. If 'all', store the
               whole dictionary.
        pert = list of variables to store from perturbations. If 'all', store
               the whole dictionary.
        trans = list of variables to store from transfer. If 'all', store
                the whole dictionary. get_transfer accept two optional
                arguments: z=0 and output_format='class' (avaible options are
                'class' or 'camb'). If different values are desired, first
                item of trans must be {'z': value, 'output_format': value}.
        pk = list with the minimum and maximum k values to store the present
             matter power spectrum and the number of points [k_min, k_max,
             number_points]. Default [10^-4, 10^1, 100].
        extra = list of any of the method or objects defined in cosmo, e.g.
                w0_smg().  It will store {'method': cosmo.w0_smg()}
        update = if True update old computed[key] dictionary elsewise create a
                 new one.  Default: True.
        cosmo_msg = if True, print cosmo.compute() messages. Default: False.
        """

        key = varied_name

        if texname:
            self.set_texnames({varied_name: texname})
        elif key not in self.texnames:  # texname will not be set at this stage. No check required
            self.set_texnames({varied_name: varied_name})

        if (not update) or (key not in self.computed.keys()):
            self.computed[key] = od()

        for val in values:
            # key = "{}={}".format(varied_name, val)
            params["parameters_smg"] = inip.vary_params(params["parameters_smg"], [[index_variable, val]])

            # It might be after the try to not store empty dictionaries.
            # Nevertheless, I find more useful having them to keep track of
            # those failed and, perhaps, to implement a method to obtain them
            # with Omega_smg_debug.
            d = self.computed[key][val] = {}

            self.cosmo.empty()
            self.cosmo.set(params)

            try:
                self.cosmo.compute()
            except Exception, e:
                print "Error: skipping {}={}".format(key, val)
                if cosmo_msg:
                    print e

                continue

            d['tunned'] = self.cosmo.get_current_derived_parameters(['tuning_parameter'])['tuning_parameter']

            for lst in [[back, 'back', self.cosmo.get_background],
                        [thermo, 'thermo', self.cosmo.get_thermodynamics],
                        [prim, 'prim', self.cosmo.get_thermodynamics]]:
                if lst[0]:
                    output = lst[2]()
                    if lst[0][0] == 'all':
                        d[lst[1]] = output
                    else:
                        d[lst[1]] = {}
                        for item in back:
                            if type(item) is list:
                                d[lst[1]].update({item[0]: output[item[0]][item[1]]})
                            else:
                                d[lst[1]].update({item: output[item]})

            if pert:
                # Perturbation is tricky because it can accept two optional
                # argument for get_perturbations and this method returns a
                # dictionary {'kind_of_pert': [{variable: list_values}]}, where
                # each item in the list is for a k (chosen in params).
                if type(pert[0]) is dict:
                    output = self.cosmo.get_perturbations(pert[0]['z'], pert[0]['output_format'])
                    if pert[1] == 'all':
                        d['pert'] = output
                else:
                    output = self.cosmo.get_perturbations()
                    if pert[0] == 'all':
                        d['pert'] = output

                if (type(pert[0]) is not dict) and (pert[0] != 'all'):
                    d['pert'] = {}
                    for subkey, lst in output.iteritems():
                        d['pert'].update({subkey: []})
                        for n, kdic in enumerate(lst):  # Each item is for a k
                            d['pert'][subkey].append({})
                            for item in pert:
                                if type(item) is list:
                                    d['pert'][subkey][n].update({item[0]: kdic[item[0]][item[1]]})
                                else:
                                    d['pert'][subkey][n].update({item: kdic[item]})

            for i in extra:
                exec('d[i] = self.cosmo.{}'.format(i))

            try:
                d['cl'] = self.__store_cl(self.cosmo.raw_cl())
            except CosmoSevereError:
                pass

            try:
                d['lcl'] = self.__store_cl(self.cosmo.lensed_cl())
            except CosmoSevereError:
                pass

            try:
                d['dcl'] = self.cosmo.density_cl()
            except CosmoSevereError:
                pass


            if ("output" in self.cosmo.pars) and ('mPk' in self.cosmo.pars['output']):
                k_array = np.linspace(*pk)
                pk_array = np.array([self.cosmo.pk(k, 0) for k in k_array])

                d['pk'] = {'k': k_array, 'pk': pk_array}

            self.cosmo.struct_cleanup()