-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Work on CCL module for cross-correlations #16
Changes from 6 commits
a517b33
da03cf7
8c1762c
135d4b3
d9060bf
151d03b
e732c3d
7d6c5da
3feb228
5c4c83a
5082354
46f21dd
47f8133
3f2551c
204503a
b26fd57
58e950f
29b2d02
7c87bf3
fad0022
e27586a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,10 @@ | |
Note that this approach preclude sharing results other than the cosmo object itself between different likelihoods. | ||
|
||
Also note lots of things still cannot be done consistently in CCL, so this is far from general. | ||
|
||
April 2021: | ||
----------- | ||
Second version by PL. Using CCL's newly implemented cosmology calculator. | ||
""" | ||
|
||
# For Cobaya docs see | ||
|
@@ -35,6 +39,7 @@ | |
import numpy as np | ||
from typing import Sequence, Union | ||
from cobaya.theory import Theory | ||
from cobaya.likelihood import Likelihood | ||
|
||
|
||
class CCL(Theory): | ||
|
@@ -45,17 +50,22 @@ class CCL(Theory): | |
z: Union[Sequence, np.ndarray] = [] # redshift sampling | ||
extra_args: dict = {} # extra (non-parameter) arguments passed to ccl.Cosmology() | ||
|
||
_default_z_sampling = np.linspace(0, 5, 100) | ||
#_default_z_sampling = np.linspace(0, 10, 150) | ||
logz = np.linspace(-3, np.log10(1100), 150) | ||
_default_z_sampling = 10**logz | ||
_default_z_sampling[0] = 0 | ||
#_default_z_sampling[-1] = 1100 | ||
|
||
def initialize(self): | ||
self.ccl = __import__("pyccl") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see above regarding importing. I think top-level import of pyccl is fine here. |
||
self._var_pairs = set() | ||
self._required_results = {} | ||
|
||
def get_requirements(self): | ||
# These are currently required to construct a CCL cosmology object. | ||
# Ultimately CCL should depend only on observable not parameters | ||
# 'As' could be substituted by sigma8. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update comment here to remove reference to 'As', since it's no longer there. |
||
return {'omch2', 'ombh2', 'ns', 'As'} | ||
return {'omch2', 'ombh2'} | ||
|
||
def must_provide(self, **requirements): | ||
# requirements is dictionary of things requested by likelihoods | ||
|
@@ -82,10 +92,10 @@ def must_provide(self, **requirements): | |
# general as placeholder | ||
self._var_pairs.update( | ||
set((x, y) for x, y in | ||
options.get('vars_pairs', [('delta_tot', 'delta_tot')]))) | ||
options.get('vars_pairs', [('delta_nonu', 'delta_nonu')]))) | ||
|
||
needs['Pk_grid'] = { | ||
'vars_pairs': self._var_pairs or [('delta_tot', 'delta_tot')], | ||
'vars_pairs': self._var_pairs or [('delta_nonu', 'delta_nonu')], | ||
'nonlinear': (True, False) if self.nonlinear else False, | ||
'z': self.z, | ||
'k_max': self.kmax | ||
|
@@ -94,6 +104,9 @@ def must_provide(self, **requirements): | |
needs['Hubble'] = {'z': self.z} | ||
needs['comoving_radial_distance'] = {'z': self.z} | ||
|
||
needs['fsigma8'] = {'z': self.z} | ||
needs['sigma8_z'] = {'z': self.z} | ||
|
||
assert len(self._var_pairs) < 2, "CCL doesn't support other Pk yet" | ||
return needs | ||
|
||
|
@@ -109,58 +122,70 @@ def calculate(self, state, want_derived=True, **params_values_dict): | |
distance = self.provider.get_comoving_radial_distance(self.z) | ||
hubble_z = self.provider.get_Hubble(self.z) | ||
H0 = hubble_z[0] | ||
h = H0/100 | ||
E_of_z = hubble_z / H0 | ||
|
||
Omega_c = self.provider.get_param('omch2') / h ** 2 | ||
Omega_b = self.provider.get_param('ombh2') / h ** 2 | ||
# Array z is sorted in ascending order. CCL requires an ascending scale factor | ||
# as input | ||
# Flip the arrays to make them a function of the increasing scale factor. | ||
# If redshift sampling is changed, check that it is monotonically increasing | ||
distance = np.flip(distance) | ||
E_of_z = np.flip(E_of_z) | ||
|
||
# Create a CCL cosmology object | ||
import pyccl as ccl | ||
h = H0 / 100. | ||
Omega_c = self.provider.get_param('omch2') / h ** 2 | ||
Omega_b = self.provider.get_param('ombh2') / h ** 2 | ||
|
||
# Currently, CCL requires the (ill-defined) linear "matter" perturbation | ||
# growth factor and rate. Because it's ill-defined, we can't get it from | ||
# Boltzmann code in general; ultimately CCL should use more physical | ||
# inputs for anything of use in general models. | ||
# For now just compute from CCL itself to keep it happy: | ||
cosmo = ccl.Cosmology(Omega_c=Omega_c, Omega_b=Omega_b, h=h, | ||
n_s=self.provider.get_param('ns'), | ||
A_s=self.provider.get_param('As')) | ||
# Array z is sorted in ascending order. CCL requires an ascending scale | ||
# factor as input | ||
a = 1. / (1 + self.z[::-1]) | ||
growth = ccl.background.growth_factor(cosmo, a) | ||
fgrowth = ccl.background.growth_rate(cosmo, a) | ||
# In order to use CCL with input arrays, the cosmology object needs | ||
# to be reset. This should be improved... | ||
cosmo = ccl.Cosmology(Omega_c=Omega_c, Omega_b=Omega_b, h=h, | ||
n_s=self.provider.get_param('ns'), | ||
A_s=self.provider.get_param('As'), **self.extra_args) | ||
cosmo._set_background_from_arrays(a_array=a, | ||
chi_array=distance, | ||
hoh0_array=E_of_z, | ||
growth_array=growth, | ||
fgrowth_array=fgrowth) | ||
#growth = ccl.background.growth_factor(cosmo, a) | ||
#fgrowth = ccl.background.growth_rate(cosmo, a) | ||
|
||
# Create a CCL cosmology object. Because we are giving it background | ||
# quantities, it should not depend on the cosmology parameters given | ||
cosmo = self.ccl.CosmologyCalculator( | ||
Omega_c=Omega_c, Omega_b=Omega_b, h=h, sigma8=0.8, n_s=0.96, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would feel a bit more comfortable with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're passing linear power spectra, sigma8 and n_s should be redundant. You're not doing that here, but see my comment below. |
||
background={'a': a, | ||
'chi': distance, | ||
'h_over_h0': E_of_z}, | ||
) | ||
|
||
if self.kmax: | ||
for pair in self._var_pairs: | ||
# Get the matter power spectrum: | ||
k, z, Pk_lin = self.provider.get_Pk_grid(var_pair=pair, nonlinear=False) | ||
fs8 = self.provider.get_fsigma8(self.z) | ||
s8 = self.provider.get_sigma8_z(self.z) | ||
growth = np.mean(Pk_lin/Pk_lin[0], axis = -1) | ||
fgrowth = fs8/s8 | ||
growth = np.flip(growth) | ||
fgrowth = np.flip(fgrowth) | ||
|
||
# Undo h units | ||
#k = kh*h | ||
#Pk_lin = Pk_lin/h**3. | ||
|
||
# np.flip(arr, axis=0) flips the rows of arr, thus making Pk with z | ||
# in descending order. | ||
Pk_lin = np.flip(Pk_lin, axis=0) | ||
cosmo._set_linear_power_from_arrays(a, k, Pk_lin) | ||
cosmo._init_pklin({'a': a, | ||
'k': k, | ||
'delta_matter:delta_matter': Pk_lin}) | ||
cosmo._init_growth({'a': a, | ||
'growth_factor': growth, | ||
'growth_rate': fgrowth}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this kosher CCL API usage? My understanding from @damonge is that growth and pk should get set as part of the initialization of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, indeed. This should be done when initializing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah that's great, I will change that, thanks! |
||
|
||
if self.nonlinear: | ||
k, z, Pk_nonlin = self.provider.get_Pk_grid(var_pair=pair, nonlinear=True) | ||
_, z, Pk_nonlin = self.provider.get_Pk_grid(var_pair=pair, nonlinear=True) | ||
Pk_nonlin = np.flip(Pk_nonlin, axis=0) | ||
cosmo._set_nonlin_power_from_arrays(a, k, Pk_nonlin) | ||
#cosmo._set_nonlin_power_from_arrays(a, k, Pk_nonlin) | ||
|
||
# Undo h units | ||
#Pk_nonlin = Pk_nonlin/h**3. | ||
|
||
cosmo._init_pknl({'a': a, | ||
'k': k, | ||
'delta_matter:delta_matter': Pk_nonlin}, | ||
has_nonlin_model = False) | ||
|
||
state['CCL'] = {'cosmo': cosmo} | ||
for required_result, method in self._required_results.items(): | ||
|
@@ -175,3 +200,61 @@ def get_CCL(self): | |
:return: dict of results | ||
""" | ||
return self._current_state['CCL'] | ||
|
||
class Tester(Likelihood): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is something that should stay here in |
||
# Cross and auto data | ||
|
||
auto_file: str = 'input/clgg_noiseless.txt' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what are these paths relative to? See some of the other likelihoods for how to package data. Options might be making it installable, or including the data in the package and finding it with |
||
cross_file: str = 'input/clkg_noiseless.txt' | ||
#auto_file: str = 'input/clgg.txt' | ||
#cross_file: str = 'input/clkg.txt' | ||
dndz_file: str = 'input/dndz.txt' | ||
|
||
params = {'b1': 1, 's1': 0.4} | ||
|
||
#ell_file: str = "/Users/Pablo/Code/SOLikeT/soliket/data/simulated_ccl/ell.npy" | ||
#cl_file: str = "/Users/Pablo/Code/SOLikeT/soliket/data/simulated_ccl/cls.npy" | ||
#dcl_file: str = "/Users/Pablo/Code/SOLikeT/soliket/data/simulated_ccl/dcls.npy" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove unused code, here and elsewhere |
||
|
||
def initialize(self): | ||
self.ccl = __import__("pyccl") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see above comment about global pyccl; I think we should stick with that. |
||
#self.cl_data = np.load(self.cl_file) | ||
#self.dcl_data = np.load(self.dcl_file) | ||
#self.ell_data = np.load(self.ell_file) | ||
data_auto = np.loadtxt(self.auto_file) | ||
data_cross = np.loadtxt(self.cross_file) | ||
self.dndz = np.loadtxt(self.dndz_file) | ||
|
||
# Get data | ||
self.ell_auto = data_auto[0] | ||
self.cl_auto = data_auto[1] | ||
self.cl_auto_err = data_auto[2] | ||
|
||
self.ell_cross = data_cross[0] | ||
self.cl_cross = data_cross[1] | ||
self.cl_cross_err = data_cross[2] | ||
|
||
def get_requirements(self): | ||
return {'CCL': {#"methods": {'theory': self._get_theory}, | ||
"kmax": 10, | ||
"nonlinear": True}} | ||
|
||
def logp(self, **pars): | ||
cosmo = self.provider.get_CCL()['cosmo'] | ||
#cosmo = results['theory'] | ||
|
||
tracer_g = self.ccl.NumberCountsTracer(cosmo, has_rsd=False, dndz = self.dndz.T, | ||
bias =(self.dndz[:,0], pars['b1']*np.ones(len(self.dndz[:,0]))), | ||
mag_bias = (self.dndz[:,0], pars['s1']*np.ones(len(self.dndz[:,0]))) | ||
) | ||
tracer_k = self.ccl.CMBLensingTracer(cosmo, z_source = 1060) | ||
|
||
cl_gg = self.ccl.cls.angular_cl(cosmo, tracer_g, tracer_g, self.ell_auto) #+ 1e-7 | ||
cl_kg = self.ccl.cls.angular_cl(cosmo, tracer_k, tracer_g, self.ell_cross) | ||
|
||
#cl_gg, cl_kg = results['theory'] | ||
delta = np.concatenate([cl_gg - self.cl_auto, cl_kg - self.cl_cross]) | ||
sigma = np.concatenate([self.cl_auto_err, self.cl_cross_err]) | ||
chi2 = delta**2/sigma**2. | ||
#chi2 = np.dot(r, self.invcov.dot(r)) | ||
return -0.5 * np.sum(chi2) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is a gaussian likelihood, this likelihood should inherit from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (see some of the other likelihoods for examples, or ask me for more clarification) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is
Tester
imported at the package top level like this? If it's something just for testing, it should just be in the test module, not imported here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also prefer putting a try/except block to import CCL here rather than the dynamic import in ccl.py; makes more sense to have the error caught at import time here rather than runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends on whether you want access to static properties of CCL class without having pyccl installed (e.g. for auto-installing it from cobaya install scripts - which is why mostly dynamical imports in the Cobaya default likelihoods).