diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 4ac4e3ff..00000000 --- a/.flake8 +++ /dev/null @@ -1,8 +0,0 @@ -[flake8] -max-line-length = 88 -per-file-ignores = - examples/srd_sn/generate_sn_data.py:E501 -... -select = C,E,F,W,B,B950 -extend-ignore = E203, E501 - diff --git a/environment.yml b/environment.yml index bdb76c1c..aa432d3d 100644 --- a/environment.yml +++ b/environment.yml @@ -13,6 +13,7 @@ dependencies: - dill - fitsio - flake8 + - flake8-docstrings - fuzzywuzzy - getdist - idna diff --git a/examples/cluster_number_counts/cluster_redshift_richness.py b/examples/cluster_number_counts/cluster_redshift_richness.py index 87a655d1..15e0799c 100644 --- a/examples/cluster_number_counts/cluster_redshift_richness.py +++ b/examples/cluster_number_counts/cluster_redshift_richness.py @@ -19,6 +19,7 @@ def get_cluster_abundance() -> ClusterAbundance: + """Creates and returns a ClusterAbundance object.""" hmf = ccl.halos.MassFuncBocquet16() min_mass, max_mass = 13.0, 16.0 min_z, max_z = 0.2, 0.8 @@ -30,10 +31,7 @@ def get_cluster_abundance() -> ClusterAbundance: def build_likelihood( build_parameters: NamedParameters, ) -> tuple[Likelihood, ModelingTools]: - """ - Here we instantiate the number density (or mass function) object. - """ - + """Builds the likelihood for Firecrown.""" # Pull params for the likelihood from build_parameters average_on = ClusterProperty.NONE if build_parameters.get_bool("use_cluster_counts", True): diff --git a/examples/cluster_number_counts/compare_integration_methods.py b/examples/cluster_number_counts/compare_integration_methods.py index 7b3798e6..cf6cd760 100644 --- a/examples/cluster_number_counts/compare_integration_methods.py +++ b/examples/cluster_number_counts/compare_integration_methods.py @@ -16,6 +16,7 @@ def get_cosmology() -> pyccl.Cosmology: + """Creates and returns a CCL cosmology object.""" Omega_c = 0.262 Omega_b = 0.049 Omega_k = 0.0 diff --git a/examples/cluster_number_counts/generate_rich_mean_mass_sacc_data.py b/examples/cluster_number_counts/generate_rich_mean_mass_sacc_data.py index 5666faf4..8839deb3 100755 --- a/examples/cluster_number_counts/generate_rich_mean_mass_sacc_data.py +++ b/examples/cluster_number_counts/generate_rich_mean_mass_sacc_data.py @@ -25,7 +25,6 @@ def generate_sacc_file() -> Any: """Generate a SACC file for cluster number counts.""" - H0 = 71.0 Ob0 = 0.0448 Odm0 = 0.22 diff --git a/examples/cosmicshear/cosmicshear.py b/examples/cosmicshear/cosmicshear.py index ad73809b..dead387f 100644 --- a/examples/cosmicshear/cosmicshear.py +++ b/examples/cosmicshear/cosmicshear.py @@ -1,5 +1,4 @@ -"""Define the likelihood factory function for the cosmic shear example. -""" +"""Define the likelihood factory function for the cosmic shear example.""" import os diff --git a/examples/cosmicshear/generate_cosmicshear_data.py b/examples/cosmicshear/generate_cosmicshear_data.py index 876b7efb..0f5dabf2 100644 --- a/examples/cosmicshear/generate_cosmicshear_data.py +++ b/examples/cosmicshear/generate_cosmicshear_data.py @@ -1,5 +1,4 @@ -""" Generate the cosmicshear.fits file. -""" +"""Generate the cosmicshear.fits file.""" import numpy as np import sacc diff --git a/examples/des_y1_3x2pt/des_y1_3x2pt.py b/examples/des_y1_3x2pt/des_y1_3x2pt.py index ee4ab498..9bf8996b 100755 --- a/examples/des_y1_3x2pt/des_y1_3x2pt.py +++ b/examples/des_y1_3x2pt/des_y1_3x2pt.py @@ -19,7 +19,6 @@ # theoretical prediction and are also constructed in the build_likelihood function. def build_likelihood(_): """Build the DES Y1 3x2pt likelihood.""" - # Creates a LAI systematic. This is a systematic that is applied to # all weak-lensing probes. The `sacc_tracer` argument is used to identify the # section of the SACC file that this systematic will be applied to. In this diff --git a/examples/des_y1_3x2pt/des_y1_3x2pt_PT.py b/examples/des_y1_3x2pt/des_y1_3x2pt_PT.py index b98add74..fbfc9094 100755 --- a/examples/des_y1_3x2pt/des_y1_3x2pt_PT.py +++ b/examples/des_y1_3x2pt/des_y1_3x2pt_PT.py @@ -33,8 +33,11 @@ @dataclass class CclSetup: - """A package of related CCL parameters, to reduce the number of variables - used in the :meth:`run_likelihood` method.""" + """A package of related CCL parameters. + + We use this to reduce the number of variables used in the :meth:`run_likelihood` + method. + """ a_1: float = 1.0 a_2: float = 0.5 @@ -47,8 +50,11 @@ class CclSetup: @dataclass class CElls: - """A package of related C_ell values, to reduce the number of variables - used in the :meth:`run_likelihood` method.""" + """A package of related C_ell values. + + This is used to reduce the number of variables used in the + :meth:`run_likelihood` method. + """ GG: np.ndarray GI: np.ndarray @@ -78,7 +84,6 @@ def __init__(self, stat0: TwoPoint, stat2: TwoPoint, stat3: TwoPoint): def build_likelihood(_) -> tuple[Likelihood, ModelingTools]: """Likelihood factory function for DES Y1 3x2pt analysis.""" - # Load sacc file sacc_data = sacc.Sacc.load_fits(saccfile) @@ -162,10 +167,7 @@ def build_likelihood(_) -> tuple[Likelihood, ModelingTools]: # We can also run the likelihood directly def run_likelihood() -> None: - """Produce some plots using the likelihood function built by - :meth:`build_likelihood`. - """ - + """Produce plots using the likelihood function built by :meth:`build_likelihood`.""" # pylint: enable=import-outside-toplevel likelihood, tools = build_likelihood(None) diff --git a/examples/des_y1_3x2pt/des_y1_cosmic_shear_TATT.py b/examples/des_y1_3x2pt/des_y1_cosmic_shear_TATT.py index a43acd78..eb836e86 100644 --- a/examples/des_y1_3x2pt/des_y1_cosmic_shear_TATT.py +++ b/examples/des_y1_3x2pt/des_y1_cosmic_shear_TATT.py @@ -94,7 +94,6 @@ def define_sources(n_source): # We can also run the likelihood directly def run_likelihood() -> None: """Run the likelihood.""" - likelihood, tools = build_likelihood(None) # Load sacc file diff --git a/examples/des_y1_3x2pt/des_y1_cosmic_shear_pk_modifier.py b/examples/des_y1_3x2pt/des_y1_cosmic_shear_pk_modifier.py index 9804e4f1..9afa9585 100644 --- a/examples/des_y1_3x2pt/des_y1_cosmic_shear_pk_modifier.py +++ b/examples/des_y1_3x2pt/des_y1_cosmic_shear_pk_modifier.py @@ -1,5 +1,7 @@ -"""Example of a Firecrown likelihood using the DES Y1 cosmic shear data with a -modified matter power spectrum""" +"""Example of a Firecrown likelihood using the DES Y1 cosmic shear data. + +This example also includes a modified matter power spectrum. +""" import os @@ -23,8 +25,10 @@ class vanDaalen19Baryonfication(PowerspectrumModifier): - """A PowerspectrumModifier class that implements the van Daalen et al. 2019 - baryon model.""" + """A PowerspectrumModifier class. + + This clas implements the van Daalen et al. 2019 baryon model. + """ name: str = "delta_matter_baryons:delta_matter_baryons" @@ -35,6 +39,7 @@ def __init__(self, pk_to_modify: str = "delta_matter:delta_matter"): self.f_bar = create() def compute_p_of_k_z(self, tools: ModelingTools) -> pyccl.Pk2D: + """Compute the 3D power spectrum P(k, z).""" self.vD19.update_parameters(fbar=self.f_bar) return self.vD19.include_baryonic_effects( cosmo=tools.get_ccl_cosmology(), pk=tools.get_pk(self.pk_to_modify) @@ -110,7 +115,6 @@ def define_sources(n_source): # We can also run the likelihood directly def run_likelihood() -> None: """Run the likelihood.""" - likelihood, tools = build_likelihood(None) # Load sacc file diff --git a/examples/des_y1_3x2pt/generate_des_data.py b/examples/des_y1_3x2pt/generate_des_data.py index ef177b7b..d8e194d0 100644 --- a/examples/des_y1_3x2pt/generate_des_data.py +++ b/examples/des_y1_3x2pt/generate_des_data.py @@ -1,4 +1,4 @@ -""" Generate the firecrown input from the DES Y1 3x2pt data products. +"""Generate the firecrown input from the DES Y1 3x2pt data products. Before running this script, you must first download the DES Y1 3x2pt data products from: diff --git a/examples/srd_sn/generate_sn_data.py b/examples/srd_sn/generate_sn_data.py index c47dbf74..a54f3a14 100644 --- a/examples/srd_sn/generate_sn_data.py +++ b/examples/srd_sn/generate_sn_data.py @@ -1,5 +1,4 @@ -"""Generate SACC data into file srd-y1-converted.sacc. -""" +"""Generate SACC data into file srd-y1-converted.sacc.""" import os import tarfile @@ -14,10 +13,9 @@ def conversion(hdg): - """ - CODE TO CONVERT THE NEW DESC HUBBLE DIAGRAM FILE STRUCTURE - SIMILAR TO COSMOMC STYLED INPUT HUBBLE DIAGRAM - FILE STRUCTURE + """Code to convert the new DESC Hubble diagram file structure. + + This is similar to cosmomc styled input hubble diagram file structure. """ col = [ "#name", @@ -61,8 +59,9 @@ def conversion(hdg): def read_hubble_data(args): - """Read the Hubble and covariance data, either from specified files or - from the default files. + """Read the Hubble and covariance data. + + This is read either from specified files or from the default files. """ if len(args) == 4: path = args[1] @@ -115,8 +114,7 @@ def read_hubble_data(args): def ensure_local_data_files(dirname_year10, url): - """Ensure we have the required local data files, downloading them if - needed.""" + """Ensure we have the required local data files, downloading them if needed.""" if os.path.exists(dirname_year10): print("Y10 directory already downloaded") else: diff --git a/examples/srd_sn/sn_srd.py b/examples/srd_sn/sn_srd.py index 331fc16c..67a0507e 100644 --- a/examples/srd_sn/sn_srd.py +++ b/examples/srd_sn/sn_srd.py @@ -1,5 +1,4 @@ -"""Demonstration of the use of the :class:`Supernova` statistics object. -""" +"""Demonstration of the use of the :class:`Supernova` statistics object.""" import sacc import firecrown.likelihood.gauss_family.statistic.supernova as sn @@ -8,8 +7,10 @@ def build_likelihood(params: NamedParameters): - """Build the Firecrown likelihood object. We have no extra tools for this - example.""" + """Build the Firecrown likelihood object. + + We have no extra tools for this example. + """ # Here we instantiate the necessary statistic object to deal with SNIa data. snia_stats = sn.Supernova(sacc_tracer="sn_ddf_sample") diff --git a/firecrown/connector/cobaya/ccl.py b/firecrown/connector/cobaya/ccl.py index ff169835..16f1b59c 100644 --- a/firecrown/connector/cobaya/ccl.py +++ b/firecrown/connector/cobaya/ccl.py @@ -1,8 +1,6 @@ -"""Cobaya CCL Connector - +"""Cobaya CCL Connector. Provide the class CCLConnector, which is an implementation of a Cobaya Theory. - """ from __future__ import annotations @@ -18,9 +16,7 @@ class CCLConnector(Theory): - """ - A class implementing cobaya.theory.Theory. - """ + """A class implementing cobaya.theory.Theory.""" input_style: Optional[str] = None @@ -30,7 +26,6 @@ def initialize(self): This is used instead of __init__, to provide default initialization. Cobaya does not allow us to override __init__. """ - assert self.input_style self.map = mapping_builder(input_style=self.input_style) @@ -51,7 +46,6 @@ def initialize_with_provider(self, provider): Sets instance's provided to the given provider. """ - self.provider = provider def get_can_provide_params(self): @@ -83,9 +77,7 @@ def get_requirements( Returns a dictionary with keys: omk, Pk_grid, comoving_radial_distance, Hubble, and with values reflecting the current status of the object. - """ - pyccl_calculator_requires = { "omk": None, "Pk_grid": {"k_max": self.Pk_kmax, "z": self.z_Pk}, @@ -105,7 +97,6 @@ def calculate( self, state: dict[str, float], want_derived=True, **params_values ) -> None: """Calculate the current cosmology, and set state["pyccl"] to the result.""" - self.map.set_params_from_camb(**params_values) pyccl_params_values = self.map.asdict() diff --git a/firecrown/connector/cobaya/likelihood.py b/firecrown/connector/cobaya/likelihood.py index 04f180af..4078ee54 100644 --- a/firecrown/connector/cobaya/likelihood.py +++ b/firecrown/connector/cobaya/likelihood.py @@ -1,4 +1,4 @@ -"""Cobaya Likelihood Connector +"""Cobaya Likelihood Connector. Module for providing a likelihood for use in Cobaya. @@ -25,8 +25,7 @@ class LikelihoodConnector(Likelihood): build_parameters: NamedParameters def initialize(self): - """Initialize the likelihood object by loading its Firecrown - configuration.""" + """Initialize the likelihood object by loading its Firecrown configuration.""" if not hasattr(self, "build_parameters"): build_parameters = NamedParameters() else: @@ -54,7 +53,6 @@ def initialize_with_provider(self, provider) -> None: Sets instance's provided to the given provider. """ - self.provider = provider def get_can_provide_params(self) -> list[str]: diff --git a/firecrown/connector/cosmosis/likelihood.py b/firecrown/connector/cosmosis/likelihood.py index 00e1ef91..f7ba9428 100644 --- a/firecrown/connector/cosmosis/likelihood.py +++ b/firecrown/connector/cosmosis/likelihood.py @@ -1,4 +1,4 @@ -"""CosmoSIS Likelihood Connector +"""CosmoSIS Likelihood Connector. This module provides the class FirecrownLikelihood, and the hook functions for this module to be a CosmoSIS likelihood module. @@ -22,8 +22,7 @@ def extract_section(sample: cosmosis.datablock, section: str) -> NamedParameters: - """Extract the all the parameters from the name datablock section into a - dictionary.""" + """Extract all the parameters from the name datablock section into a dictionary.""" if not sample.has_section(section): raise RuntimeError(f"Datablock section `{section}' does not exist.") sec_dict = {name: sample[section, name] for _, name in sample.keys(section=section)} @@ -183,9 +182,11 @@ def execute(self, sample: cosmosis.datablock) -> int: return 0 def form_error_message(self, exc: MissingSamplerParameterError) -> str: - """Form the error message that will be used to report a missing - parameter, when that parameter should have been supplied by the - sampler.""" + """Form the error message that will be used to report a missing parameter. + + This error message will also include when that parameter should have been + supplied by the sampler. + """ msg = ( "A required parameter was not found in any of the " "sections searched on DataBlock.\n" @@ -203,7 +204,6 @@ def form_error_message(self, exc: MissingSamplerParameterError) -> str: def calculate_firecrown_params(self, sample: cosmosis.datablock) -> ParamsMap: """Calculate the ParamsMap for this sample.""" - firecrown_params = ParamsMap() for section in self.sampling_sections: section_params = extract_section(sample, section) @@ -222,16 +222,21 @@ def calculate_firecrown_params(self, sample: cosmosis.datablock) -> ParamsMap: def setup(config: cosmosis.datablock) -> FirecrownLikelihood: - """Setup hook for a CosmoSIS module. Returns an instance of + """Setup hook for a CosmoSIS module. + + Returns an instance of class FirecrownLikelihood. The same object will be passed to the CosmoSIS - execute hook.""" + execute hook. + """ return FirecrownLikelihood(config) def execute(sample: cosmosis.datablock, instance: FirecrownLikelihood) -> int: - """Execute hook for a CosmoSIS module. Return 0 on success. The parameter - `sample` represents the current MCMC sample; `instance` is the - FirecrownLikelihood object created by `setup`.""" + """Execute hook for a CosmoSIS module. + + Return 0 on success. The parameter `sample` represents the current MCMC sample; + `instance` is the FirecrownLikelihood object created by `setup`. + """ return instance.execute(sample) diff --git a/firecrown/connector/mapping.py b/firecrown/connector/mapping.py index 54769251..0597d295 100644 --- a/firecrown/connector/mapping.py +++ b/firecrown/connector/mapping.py @@ -63,9 +63,7 @@ def __init__(self, *, require_nonlinear_pk: bool = False): self.m_nu: Optional[Union[float, list[float]]] = None def get_params_names(self) -> list[str]: - """Return the names of the cosmological parameters that this - mapping is expected to deliver. - """ + """Return the names of the expected cosmological parameters for this mapping.""" warnings.simplefilter("always", DeprecationWarning) warnings.warn( "This method is implementation specific and should only be " @@ -87,7 +85,7 @@ def transform_k_h_to_k(self, k_h): ) def transform_p_k_h3_to_p_k(self, p_k_h3): - """Transform the given :math:`p_k h^3 \\to p_k`.""" + r"""Transform the given :math:`p_k h^3 \to p_k`.""" assert p_k_h3 is not None # use assertion to silence pylint warning warnings.simplefilter("always", DeprecationWarning) warnings.warn( @@ -126,12 +124,11 @@ def set_params( wa: float, T_CMB: float, ): - """Sets the cosmological constants suitable for use in constructing a - pyccl.core.CosmologyCalculator. See the documentation of that class - for an explanation of the choices and meanings of default values - of None. - """ + """Sets the cosmological constants suitable a pyccl.core.CosmologyCalculator. + See the documentation of that class for an explanation of the choices and + meanings of default values of None. + """ # Typecheck is done automatically using the descriptors and is done to # avoid void very confusing error messages at a later time in case of # error. @@ -160,18 +157,22 @@ def set_params( @staticmethod def redshift_to_scale_factor(z): - """Given arrays of redshift returns an array of scale factor with the - inverse order.""" + """Converts redshift to scale factor. + Given arrays of redshift returns an array of scale factor with the + inverse order. + """ scale = np.flip(1.0 / (1.0 + z)) return scale @staticmethod def redshift_to_scale_factor_p_k(p_k): - """Given an 2d arrays power spectrum ordered by (redshift, mode) - return a 2d array with the rows flipped to match the reorderning - from redshift to scale factor.""" + """Converts power spectrum from redshift to scale factor. + Given an 2d arrays power spectrum ordered by (redshift, mode) + return a 2d array with the rows flipped to match the reorderning + from redshift to scale factor. + """ p_k_out = np.flipud(p_k) return p_k_out @@ -211,6 +212,7 @@ class MappingCosmoSIS(Mapping): """Mapping support for CosmoSIS.""" def get_params_names(self): + """Return the names of the expected cosmological parameters for this mapping.""" return [ "h0", "omega_b", @@ -225,18 +227,24 @@ def get_params_names(self): ] def transform_k_h_to_k(self, k_h): + """Transform the given k_h (k over h) to k.""" return k_h * self.h def transform_p_k_h3_to_p_k(self, p_k_h3): + r"""Transform the given :math:`p_k h^3 \to p_k`.""" return p_k_h3 / (self.h**3) def transform_h_to_h_over_h0(self, h): + """Transform distances h to :math:`h/h_0`.""" hubble_radius_today = physics.CLIGHT * 1e-5 / self.h return np.flip(h) * hubble_radius_today def set_params_from_cosmosis(self, cosmosis_params: NamedParameters): - """Return a PyCCLCosmologyConstants object with parameters equivalent to - those read from CosmoSIS when using CAMB.""" + """Return a PyCCLCosmologyConstants object. + + This object has parameters equivalent to those read from CosmoSIS when using + CAMB. + """ # TODO: Verify that CosmoSIS/CAMB does not use Omega_g # TODO: Verify that CosmoSIS/CAMB uses delta_neff, not N_eff @@ -354,9 +362,7 @@ class MappingCAMB(Mapping): """ def get_params_names(self) -> list[str]: - """ - Return the list of parameters handled by this mapping. - """ + """Return the list of parameters handled by this mapping.""" return [ "H0", "ombh2", @@ -372,8 +378,10 @@ def get_params_names(self) -> list[str]: ] def set_params_from_camb(self, **params_values): - """Read the CAMB-style parameters from params_values, translate them to - our conventions, and store them.""" + """Read the CAMB-style parameters from params_values. + + Then, translate them to our conventions, and store them. + """ # pylint: disable-msg=R0914 # CAMB can use different parameters in place of H0, we must deal with this @@ -430,9 +438,10 @@ def set_params_from_camb(self, **params_values): def mapping_builder(*, input_style: str, **kwargs): - """Return the Mapping class for the given input_style. If input_style is not - recognized raise an exception.""" + """Return the Mapping class for the given input_style. + If input_style is not recognized raise an exception. + """ if input_style not in mapping_classes: raise ValueError(f"input_style must be {*mapping_classes, }, not {input_style}") diff --git a/firecrown/connector/numcosmo/__init__.py b/firecrown/connector/numcosmo/__init__.py index e69de29b..a17bb21d 100644 --- a/firecrown/connector/numcosmo/__init__.py +++ b/firecrown/connector/numcosmo/__init__.py @@ -0,0 +1 @@ +"""Module with classes for the numcosmo connector.""" diff --git a/firecrown/connector/numcosmo/numcosmo.py b/firecrown/connector/numcosmo/numcosmo.py index 3c127d71..eb811d20 100644 --- a/firecrown/connector/numcosmo/numcosmo.py +++ b/firecrown/connector/numcosmo/numcosmo.py @@ -2,7 +2,6 @@ The subpackages and modules in this package depend upon NumCosmo, and can not be used without an installation of NumCosmo. - """ from typing import Union, Any, Optional @@ -21,9 +20,12 @@ class MappingNumCosmo(GObject.Object): - """Mapping support for NumCosmo, this is a subclass of :class:`Mapping` that - provides a mapping from a NumCosmo Cosmological model to a CCL cosmology. - It also converts NumCosmo models to :class:`ParamsMap` objects.""" + """Mapping support for NumCosmo. + + This is a subclass of :class:`Mapping` that provides a mapping from a NumCosmo + Cosmological model to a CCL cosmology. It also converts NumCosmo models to + :class:`ParamsMap` objects. + """ __gtype_name__ = "FirecrownMappingNumCosmo" @@ -131,9 +133,10 @@ def _set_dist(self, value: Nc.Distance): def set_params_from_numcosmo( self, mset: Ncm.MSet ): # pylint: disable-msg=too-many-locals - """Return a PyCCLCosmologyConstants object with parameters equivalent to - those read from NumCosmo.""" + """Return a PyCCLCosmologyConstants object. + This object will have parameters equivalent to those read from NumCosmo. + """ hi_cosmo = mset.peek(Nc.HICosmo.id()) assert isinstance(hi_cosmo, Nc.HICosmo) @@ -271,7 +274,6 @@ def calculate_ccl_args(self, mset: Ncm.MSet): # pylint: disable-msg=too-many-lo def create_params_map(self, model_list: list[str], mset: Ncm.MSet) -> ParamsMap: """Create a ParamsMap from a NumCosmo MSet.""" - params_map = ParamsMap() for model_ns in model_list: mid = mset.get_id_by_ns(model_ns) @@ -299,9 +301,11 @@ def create_params_map(self, model_list: list[str], mset: Ncm.MSet) -> ParamsMap: class NumCosmoData(Ncm.Data): - """NumCosmoData is a subclass of Ncm.Data and implements NumCosmo likelihood - object virtual methods using the prefix `do_`. This class implements - a general likelihood.""" + """NumCosmoData is a subclass of Ncm.Data. + + This subclass also implements NumCosmo likelihood object virtual methods using + the prefix `do_`. This class implements a general likelihood. + """ __gtype_name__ = "FirecrownNumCosmoData" @@ -367,7 +371,6 @@ def _get_likelihood_source(self) -> Optional[str]: def _set_likelihood_source(self, value: Optional[str]): """Set the likelihood string defining the factory function.""" - if value is not None: self._likelihood_source = value if self._starting_deserialization: @@ -422,9 +425,10 @@ def new_from_likelihood( likelihood_source: Optional[str] = None, likelihood_build_parameters: Optional[NamedParameters] = None, ): - """Initialize a NumCosmoGaussCov object representing a Gaussian likelihood - with a constant covariance.""" + """Initialize a NumCosmoGaussCov object. + This object represents a Gaussian likelihood with a constant covariance. + """ nc_data: NumCosmoData = GObject.new( cls, model_list=model_list, @@ -441,20 +445,16 @@ def new_from_likelihood( return nc_data def do_get_length(self): # pylint: disable-msg=arguments-differ - """ - Implements the virtual Ncm.Data method get_length. - """ + """Implements the virtual Ncm.Data method get_length.""" return self.len def do_get_dof(self): # pylint: disable-msg=arguments-differ - """ - Implements the virtual Ncm.Data method get_dof. - """ + """Implements the virtual Ncm.Data method get_dof.""" return self.dof def do_begin(self): # pylint: disable-msg=arguments-differ - """ - Implements the virtual Ncm.Data method `begin`. + """Implements the virtual Ncm.Data method `begin`. + This method usually do some groundwork in the data before the actual calculations. For example, if the likelihood involves the decomposition of a constant matrix, it can be done @@ -462,8 +462,8 @@ def do_begin(self): # pylint: disable-msg=arguments-differ """ def do_prepare(self, mset: Ncm.MSet): # pylint: disable-msg=arguments-differ - """ - Implements the virtual method Ncm.Data `prepare`. + """Implements the virtual method Ncm.Data `prepare`. + This method should do all the necessary calculations using mset to be able to calculate the likelihood afterwards. """ @@ -483,8 +483,8 @@ def do_prepare(self, mset: Ncm.MSet): # pylint: disable-msg=arguments-differ self.tools.prepare(self.ccl_cosmo) def do_m2lnL_val(self, _): # pylint: disable-msg=arguments-differ - """ - Implements the virtual method `m2lnL`. + """Implements the virtual method `m2lnL`. + This method should calculate the value of the likelihood for the model set `mset`. """ @@ -493,17 +493,20 @@ def do_m2lnL_val(self, _): # pylint: disable-msg=arguments-differ class NumCosmoGaussCov(Ncm.DataGaussCov): - """NumCosmoData is a subclass of Ncm.Data and implements NumCosmo likelihood - object virtual methods using the prefix `do_`. This class implements - a Gaussian likelihood.""" + """NumCosmoGaussCov is a subclass of Ncm.DataGaussCov. + + This subclass implements NumCosmo likelihood object virtual methods using the + prefix `do_`. This class implements a Gaussian likelihood. + """ __gtype_name__ = "FirecrownNumCosmoGaussCov" def __init__(self): - """Initialize a NumCosmoGaussCov object. This class is a subclass of - Ncm.DataGaussCov and implements NumCosmo likelihood object virtual - methods using the prefix `do_`. This class implements a Gaussian - likelihood. + """Initialize a NumCosmoGaussCov object. + + This class is a subclass of Ncm.DataGaussCov and implements NumCosmo + likelihood object virtual methods using the prefix `do_`. This class + implements a Gaussian likelihood. Due to the way GObject works, the constructor must have a `**kwargs` argument, and the properties must be set after construction. @@ -597,7 +600,6 @@ def _get_likelihood_source(self) -> Optional[str]: def _set_likelihood_source(self, value: Optional[str]): """Set the likelihood string defining the factory function.""" - if value is not None: self._likelihood_source = value if self._starting_deserialization: @@ -651,9 +653,10 @@ def new_from_likelihood( likelihood_source: Optional[str] = None, likelihood_build_parameters: Optional[NamedParameters] = None, ): - """Initialize a NumCosmoGaussCov object representing a Gaussian likelihood - with a constant covariance.""" + """Initialize a NumCosmoGaussCov object. + This object represents a Gaussian likelihood with a constant covariance. + """ cov = likelihood.get_cov() nrows, ncols = cov.shape assert nrows == ncols @@ -679,29 +682,25 @@ def new_from_likelihood( return nc_gauss_cov def do_get_length(self): # pylint: disable-msg=arguments-differ - """ - Implements the virtual `Ncm.Data` method `get_length`. - """ + """Implements the virtual `Ncm.Data` method `get_length`.""" return self.len def do_get_dof(self): # pylint: disable-msg=arguments-differ - """ - Implements the virtual `Ncm.Data` method `get_dof`. - """ + """Implements the virtual `Ncm.Data` method `get_dof`.""" return self.dof def do_begin(self): # pylint: disable-msg=arguments-differ - """ - # Implements the virtual `Ncm.Data` method `begin`. - # This method usually do some groundwork in the data - # before the actual calculations. For example, if the likelihood - # involves the decomposition of a constant matrix, it can be done - # during `begin` once and then used afterwards. + """Implements the virtual `Ncm.Data` method `begin`. + + This method usually do some groundwork in the data + before the actual calculations. For example, if the likelihood + involves the decomposition of a constant matrix, it can be done + during `begin` once and then used afterwards. """ def do_prepare(self, mset: Ncm.MSet): # pylint: disable-msg=arguments-differ - """ - Implements the virtual method Ncm.Data `prepare`. + """Implements the virtual method Ncm.Data `prepare`. + This method should do all the necessary calculations using mset to be able to calculate the likelihood afterwards. """ @@ -723,8 +722,8 @@ def do_prepare(self, mset: Ncm.MSet): # pylint: disable-msg=arguments-differ # pylint: disable-next=arguments-differ def do_mean_func(self, _, mean_vector): - """ - Implements the virtual `Ncm.DataGaussCov` method `mean_func`. + """Implements the virtual `Ncm.DataGaussCov` method `mean_func`. + This method should compute the theoretical mean for the gaussian distribution. """ @@ -742,8 +741,11 @@ def do_mean_func(self, _, mean_vector): class NumCosmoFactory: - """NumCosmo likelihood class. This class provide the necessary factory methods - to create NumCosmo+firecrown likelihoods.""" + """NumCosmo likelihood class. + + This class provide the necessary factory methods + to create NumCosmo+firecrown likelihoods. + """ def __init__( self, diff --git a/firecrown/descriptors.py b/firecrown/descriptors.py index 85759149..edf91fcd 100644 --- a/firecrown/descriptors.py +++ b/firecrown/descriptors.py @@ -1,6 +1,5 @@ """Provides type validation as used in connectors. - Validators are created using the constructor for each class. Access to the data done through the object name, not through any named function. Setting the data is validated with the class's `validate` function; the user does @@ -43,7 +42,9 @@ def __init__( self.allow_none = allow_none def validate(self, value: Optional[float]) -> None: - """Raise an exception if the provided `value` does not meet all of the + """Run all validators on this value. + + Raise an exception if the provided `value` does not meet all of the required conditions enforced by this validator. """ if self.allow_none and value is None: @@ -58,8 +59,7 @@ def validate(self, value: Optional[float]) -> None: raise ValueError("NaN is disallowed in a constrained float") def _is_constrained(self) -> bool: - """Return true if this validation enforces any constraint, and false - if it does not.""" + """Return if this validation enforces any constraint.""" return not ((self.minvalue is None) and (self.maxvalue is None)) def __set_name__(self, _, name: str) -> None: @@ -69,13 +69,15 @@ def __set_name__(self, _, name: str) -> None: def __get__(self, obj, objtype=None) -> float: """Accessor method, which reads controlled value. - This is invoked whenever the validated variable is read.""" + This is invoked whenever the validated variable is read. + """ return getattr(obj, self.private_name) def __set__(self, obj, value: Optional[float]) -> None: """Setter for the validated variable. - This function invokes the `validate` method of the derived class.""" + This function invokes the `validate` method of the derived class. + """ self.validate(value) setattr(obj, self.private_name, value) @@ -97,13 +99,15 @@ def __init__( maxsize: Optional[int] = None, predicate: Optional[Callable[[str], bool]] = None, ) -> None: - """Initialize the TypeString object'""" + """Initialize the TypeString object.""" self.minsize = minsize self.maxsize = maxsize self.predicate = predicate def validate(self, value: Optional[str]) -> None: - """Raise an exception if the provided `value` does not meet all of the + """Run all validators on this value. + + Raise an exception if the provided `value` does not meet all of the required conditions enforced by this validator. """ if not isinstance(value, str): @@ -126,12 +130,14 @@ def __set_name__(self, _, name: str) -> None: def __get__(self, obj, objtype=None) -> str: """Accessor method, which reads controlled value. - This is invoked whenever the validated variable is read.""" + This is invoked whenever the validated variable is read. + """ return getattr(obj, self.private_name) def __set__(self, obj, value: Optional[str]) -> None: """Setter for the validated variable. - This function invokes the `validate` method of the derived class.""" + This function invokes the `validate` method of the derived class. + """ self.validate(value) setattr(obj, self.private_name, value) diff --git a/firecrown/likelihood/gauss_family/gauss_family.py b/firecrown/likelihood/gauss_family/gauss_family.py index c0b39580..85fcccd4 100644 --- a/firecrown/likelihood/gauss_family/gauss_family.py +++ b/firecrown/likelihood/gauss_family/gauss_family.py @@ -1,10 +1,6 @@ -""" - -Gaussian Family Module -====================== +"""Gaussian Family Module. Some notes. - """ from __future__ import annotations @@ -56,8 +52,9 @@ def enforce_states( terminal: Optional[State] = None, failure_message: str, ) -> Callable[[Callable[P, T]], Callable[P, T]]: - """This decorator wraps a method, and enforces state machine behavior. If - the object is not in one of the states in initial, an + """This decorator wraps a method, and enforces state machine behavior. + + If the object is not in one of the states in initial, an AssertionError is raised with the given failure_message. If terminal is None the state of the object is not modified. If terminal is not None and the call to the wrapped method returns @@ -70,14 +67,17 @@ def enforce_states( initials = [initial] def decorator_enforce_states(func: Callable[P, T]) -> Callable[P, T]: - """This part of the decorator is the closure that actually contains the - values of initials, terminal, and failure_message. + """Part of the decorator which is the closure. + + This closure is what actually contains the values of initials, terminal, and + failure_message. """ @wraps(func) def wrapper_repeat(*args: P.args, **kwargs: P.kwargs) -> T: - """This part of the decorator is the actual wrapped method. It is - responsible for confirming a correct initial state, and + """Part of the decorator which is the actual wrapped method. + + It is responsible for confirming a correct initial state, and establishing the correct final state if the wrapped method succeeds. """ @@ -102,10 +102,10 @@ def wrapper_repeat(*args: P.args, **kwargs: P.kwargs) -> T: class GaussFamily(Likelihood): - """GaussFamily is an abstract class. It is the base class for all likelihoods - based on a chi-squared calculation. It provides an implementation of - Likelihood.compute_chisq. Derived classes must implement the abstract method - compute_loglike, which is inherited from Likelihood. + """GaussFamily is the base class for likelihoods based on a chi-squared calculation. + + It provides an implementation of Likelihood.compute_chisq. Derived classes must + implement the abstract method compute_loglike, which is inherited from Likelihood. GaussFamily (and all classes that inherit from it) must abide by the the following rules regarding the order of calling of methods. @@ -159,11 +159,13 @@ def __init__( failure_message="read() must be called before update()", ) def _update(self, _: ParamsMap) -> None: - """Handle the state resetting required by :class:`GaussFamily` - likelihoods. Any derived class that needs to implement :meth:`_update` + """Handle the state resetting required by :class:`GaussFamily` likelihoods. + + Any derived class that needs to implement :meth:`_update` for its own reasons must be sure to do what this does: check the state at the start of the method, and change the state at the end of the - method.""" + method. + """ @enforce_states( initial=[State.UPDATED, State.COMPUTED], @@ -171,11 +173,13 @@ def _update(self, _: ParamsMap) -> None: failure_message="update() must be called before reset()", ) def _reset(self) -> None: - """Handle the state resetting required by :class:`GaussFamily` - likelihoods. Any derived class that needs to implement :meth:`reset` + """Handle the state resetting required by :class:`GaussFamily` likelihoods. + + Any derived class that needs to implement :meth:`reset` for its own reasons must be sure to do what this does: check the state at the start of the method, and change the state at the end of the - method.""" + method. + """ self.theory_vector = None @enforce_states( @@ -185,7 +189,6 @@ def _reset(self) -> None: ) def read(self, sacc_data: sacc.Sacc) -> None: """Read the covariance matrix for this likelihood from the SACC file.""" - if sacc_data.covariance is None: msg = ( f"The {type(self).__name__} likelihood requires a covariance, " @@ -258,8 +261,7 @@ def get_cov( failure_message="read() must be called before get_data_vector()", ) def get_data_vector(self) -> npt.NDArray[np.float64]: - """Get the data vector from all statistics and concatenate in the right - order.""" + """Get the data vector from all statistics in the right order.""" assert self.data_vector is not None return self.data_vector @@ -287,8 +289,7 @@ def compute_theory_vector(self, tools: ModelingTools) -> npt.NDArray[np.float64] "get_theory_vector()", ) def get_theory_vector(self) -> npt.NDArray[np.float64]: - """Get the theory vector from all statistics and concatenate in the right - order.""" + """Get the theory vector from all statistics in the right order.""" assert ( self.theory_vector is not None ), "theory_vector is None after compute_theory_vector() has been called" @@ -343,8 +344,10 @@ def compute_chisq(self, tools: ModelingTools) -> float: def get_sacc_indices( self, statistic: Union[Statistic, list[Statistic], None] = None ) -> npt.NDArray[np.int64]: - """Get the SACC indices of the statistic or list of statistics. If no - statistic is given, get the indices of all statistics of the likelihood.""" + """Get the SACC indices of the statistic or list of statistics. + + If no statistic is given, get the indices of all statistics of the likelihood. + """ if statistic is None: statistic = [stat.statistic for stat in self.statistics] if isinstance(statistic, Statistic): @@ -367,6 +370,7 @@ def get_sacc_indices( def make_realization( self, sacc_data: sacc.Sacc, add_noise: bool = True, strict: bool = True ) -> sacc.Sacc: + """Create a new realization of the model.""" sacc_indices = self.get_sacc_indices() if add_noise: diff --git a/firecrown/likelihood/gauss_family/gaussian.py b/firecrown/likelihood/gauss_family/gaussian.py index 85280767..a0839c61 100644 --- a/firecrown/likelihood/gauss_family/gaussian.py +++ b/firecrown/likelihood/gauss_family/gaussian.py @@ -1,6 +1,4 @@ -"""Provides GaussFamily concrete types. - -""" +"""Provides GaussFamily concrete types.""" from __future__ import annotations import numpy as np @@ -14,10 +12,10 @@ class ConstGaussian(GaussFamily): def compute_loglike(self, tools: ModelingTools): """Compute the log-likelihood.""" - return -0.5 * self.compute_chisq(tools) def make_realization_vector(self) -> np.ndarray: + """Create a new realization of the model.""" theory_vector = self.get_theory_vector() assert self.cholesky is not None new_data_vector = theory_vector + np.dot( diff --git a/firecrown/likelihood/gauss_family/statistic/binned_cluster_number_counts.py b/firecrown/likelihood/gauss_family/statistic/binned_cluster_number_counts.py index ac8ad623..360ec0c3 100644 --- a/firecrown/likelihood/gauss_family/statistic/binned_cluster_number_counts.py +++ b/firecrown/likelihood/gauss_family/statistic/binned_cluster_number_counts.py @@ -1,4 +1,4 @@ -"""This module holds classes needed to predict the binned cluster number counts +"""This module holds classes needed to predict the binned cluster number counts. The binned cluster number counts statistic predicts the number of galaxy clusters within a single redshift and mass bin. @@ -25,7 +25,7 @@ class BinnedClusterNumberCounts(Statistic): - """The Binned Cluster Number Counts statistic + """The Binned Cluster Number Counts statistic. This class will make a prediction for the number of clusters in a z, mass bin and compare that prediction to the data provided in the sacc file. @@ -49,6 +49,7 @@ def __init__( self.bins: list[SaccBin] = [] def read(self, sacc_data: sacc.Sacc) -> None: + """Read the data for this statistic and mark it as ready for use.""" # Build the data vector and indices needed for the likelihood if self.cluster_properties == ClusterProperty.NONE: raise ValueError("You must specify at least one cluster property.") @@ -75,10 +76,12 @@ def read(self, sacc_data: sacc.Sacc) -> None: super().read(sacc_data) def get_data_vector(self) -> DataVector: + """Gets the statistic data vector.""" assert self.data_vector is not None return self.data_vector def _compute_theory_vector(self, tools: ModelingTools) -> TheoryVector: + """Compute a statistic from sources, concrete implementation.""" assert tools.cluster_abundance is not None theory_vector_list: list[float] = [] @@ -107,11 +110,12 @@ def get_binned_cluster_property( cluster_counts: list[float], cluster_properties: ClusterProperty, ) -> list[float]: - """Computes the mean mass of clusters in each bin + """Computes the mean mass of clusters in each bin. Using the data from the sacc file, this function evaluates the likelihood for a single point of the parameter space, and returns the predicted mean mass of - the clusters in each bin.""" + the clusters in each bin. + """ assert tools.cluster_abundance is not None mean_values = [] @@ -127,11 +131,12 @@ def get_binned_cluster_property( return mean_values def get_binned_cluster_counts(self, tools: ModelingTools) -> list[float]: - """Computes the number of clusters in each bin + """Computes the number of clusters in each bin. Using the data from the sacc file, this function evaluates the likelihood for a single point of the parameter space, and returns the predicted number of - clusters in each bin.""" + clusters in each bin. + """ assert tools.cluster_abundance is not None cluster_counts = [] diff --git a/firecrown/likelihood/gauss_family/statistic/source/number_counts.py b/firecrown/likelihood/gauss_family/statistic/source/number_counts.py index 0f8ded3f..7dcb2ebe 100644 --- a/firecrown/likelihood/gauss_family/statistic/source/number_counts.py +++ b/firecrown/likelihood/gauss_family/statistic/source/number_counts.py @@ -1,6 +1,4 @@ -"""Number counts source and systematics - -""" +"""Number counts source and systematics.""" from __future__ import annotations from typing import Optional, final @@ -48,7 +46,8 @@ class NumberCountsArgs(SourceGalaxyArgs): class NumberCountsSystematic(SourceGalaxySystematic[NumberCountsArgs]): """Abstract base class for systematics for Number Counts sources. - Derived classes must implement :python`apply` with the correct signature.""" + Derived classes must implement :python`apply` with the correct signature. + """ @abstractmethod def apply( @@ -62,7 +61,7 @@ class PhotoZShift(SourceGalaxyPhotoZShift[NumberCountsArgs]): class SelectField(SourceGalaxySelectField[NumberCountsArgs]): - """Systematic to select 3D field""" + """Systematic to select 3D field.""" class LinearBiasSystematic(NumberCountsSystematic): @@ -105,7 +104,6 @@ def apply( tracer_arg : NumberCountsArgs The source to which apply the shear bias. """ - ccl_cosmo = tools.get_ccl_cosmology() pref = ((1.0 + tracer_arg.z) / (1.0 + self.z_piv)) ** self.alphaz pref *= ( @@ -206,7 +204,6 @@ def apply( :return: a NumberCountsArgs object """ - z_bar = self.z_c + self.z_m * (self.r_lim - 24.0) # The slope of log(n_tot(z,r_lim)) with respect to r_lim # where n_tot(z,r_lim) is the luminosity function after using fit (C.1) @@ -300,7 +297,8 @@ def __init__( def _update_source(self, params: ParamsMap): """Perform any updates necessary after the parameters have being updated. - This implementation must update all contained Updatable instances.""" + This implementation must update all contained Updatable instances. + """ self.systematics.update(params) @final @@ -326,7 +324,6 @@ def _read(self, sacc_data): sacc_data : sacc.Sacc The data in the sacc format. """ - self.tracer_args = NumberCountsArgs( scale=self.scale, z=np.array([]), @@ -337,6 +334,7 @@ def _read(self, sacc_data): super()._read(sacc_data) def create_tracers(self, tools: ModelingTools): + """Create the tracers for this source.""" tracer_args = self.tracer_args tracer_args = replace(tracer_args, bias=self.bias * np.ones_like(tracer_args.z)) @@ -399,5 +397,6 @@ def create_tracers(self, tools: ModelingTools): return tracers, tracer_args def get_scale(self): + """Return the scale for this source.""" assert self.current_tracer_args return self.current_tracer_args.scale diff --git a/firecrown/likelihood/gauss_family/statistic/source/source.py b/firecrown/likelihood/gauss_family/statistic/source/source.py index 2b724a17..32069f62 100644 --- a/firecrown/likelihood/gauss_family/statistic/source/source.py +++ b/firecrown/likelihood/gauss_family/statistic/source/source.py @@ -1,5 +1,4 @@ -"""Abstract base classes for TwoPoint Statistics sources. -""" +"""Abstract base classes for TwoPoint Statistics sources.""" from __future__ import annotations from typing import Optional, Sequence, final, TypeVar, Generic @@ -24,11 +23,11 @@ class SourceSystematic(Updatable): """An abstract systematic class (e.g., shear biases, photo-z shifts, etc.). This class currently has no methods at all, because the argument types for - the `apply` method of different subclasses are different.""" + the `apply` method of different subclasses are different. + """ def read(self, sacc_data: sacc.Sacc): - """This method is called to allow the systematic object to read from the - appropriated sacc data.""" + """Call to allow this object to read from the appropriate sacc data.""" class Source(Updatable): @@ -67,16 +66,19 @@ def _read(self, sacc_data: sacc.Sacc): """Abstract method to read the data for this source from the SACC file.""" def _update_source(self, params: ParamsMap): - """Method to update the source from the given ParamsMap. Any subclass - that needs to do more than update its contained :class:`Updatable` - objects should implement this method.""" + """Method to update the source from the given ParamsMap. + + Any subclass that needs to do more than update its contained :class:`Updatable` + objects should implement this method. + """ @final def _update(self, params: ParamsMap): """Implementation of Updatable interface method `_update`. This clears the current hash and tracer, and calls the abstract method - `_update_source`, which must be implemented in all subclasses.""" + `_update_source`, which must be implemented in all subclasses. + """ self.cosmo_hash = None self.tracers = [] self._update_source(params) @@ -87,16 +89,15 @@ def get_scale(self) -> float: @abstractmethod def create_tracers(self, tools: ModelingTools): - """Abstract method to create tracers for this `Source`, for the given - cosmology.""" + """Create tracers for this `Source`, for the given cosmology.""" @final def get_tracers(self, tools: ModelingTools) -> Sequence[Tracer]: """Return the tracer for the given cosmology. This method caches its result, so if called a second time with the same - cosmology, no calculation needs to be done.""" - + cosmology, no calculation needs to be done. + """ ccl_cosmo = tools.get_ccl_cosmology() cur_hash = hash(ccl_cosmo) @@ -109,12 +110,17 @@ def get_tracers(self, tools: ModelingTools) -> Sequence[Tracer]: class Tracer: - """Bundles together a pyccl.Tracer object with optional information about the - underlying 3D field, a pyccl.nl_pt.PTTracer, and halo profiles.""" + """Extending the pyccl.Tracer object with additional information. + + Bundles together a pyccl.Tracer object with optional information about the + underlying 3D field, a pyccl.nl_pt.PTTracer, and halo profiles. + """ @staticmethod def determine_field_name(field: Optional[str], tracer: Optional[str]) -> str: - """This function encapsulates the policy for determining the value to be + """Gets a field name for a tracer. + + This function encapsulates the policy for determining the value to be assigned to the :attr:`field` attribute of a :class:`Tracer`. It is a static method only to keep it grouped with the class for which it is @@ -135,8 +141,7 @@ def __init__( halo_profile: Optional[pyccl.halos.HaloProfile] = None, halo_2pt: Optional[pyccl.halos.Profile2pt] = None, ): - """Initialize a new Tracer based on the given pyccl.Tracer which must not be - None. + """Initialize a new Tracer based on the pyccl.Tracer which must not be None. Note that the :class:`pyccl.Tracer` is not copied; we store a reference to the original tracer. Be careful not to accidentally share :class:`pyccl.Tracer`s. @@ -225,7 +230,6 @@ def __init__(self, sacc_tracer: str) -> None: def apply(self, tools: ModelingTools, tracer_arg: _SourceGalaxyArgsT): """Apply a shift to the photo-z distribution of a source.""" - dndz_interp = Akima1DInterpolator(tracer_arg.z, tracer_arg.dndz) dndz = dndz_interp(tracer_arg.z - self.delta_z, extrapolate=False) @@ -240,14 +244,15 @@ def apply(self, tools: ModelingTools, tracer_arg: _SourceGalaxyArgsT): class SourceGalaxySelectField( SourceGalaxySystematic[_SourceGalaxyArgsT], Generic[_SourceGalaxyArgsT] ): - """A systematic that allows specifying the 3D field that will be used + """The source galaxy select field systematic. + + A systematic that allows specifying the 3D field that will be used to select the 3D power spectrum when computing the angular power spectrum. """ def __init__(self, field: str = "delta_matter"): - """Specify which 3D field should be used when computing angular power - spectra. + """Specify which 3D field should be used when computing angular power spectra. :param field: the name of the 3D field that is associated to the tracer. Default: `"delta_matter"` @@ -258,6 +263,7 @@ def __init__(self, field: str = "delta_matter"): def apply( self, tools: ModelingTools, tracer_arg: _SourceGalaxyArgsT ) -> _SourceGalaxyArgsT: + """Apply method to include systematics in the tracer_arg.""" return replace(tracer_arg, field=self.field) @@ -287,9 +293,10 @@ def __init__( def _read(self, sacc_data: sacc.Sacc): """Read the galaxy redshift distribution model from a sacc file. - All derived classes must call this method in their own `_read` method - after they have read their own data and initialized their tracer_args.""" + All derived classes must call this method in their own `_read` method + after they have read their own data and initialized their tracer_args. + """ try: tracer_args = self.tracer_args except AttributeError as exc: diff --git a/firecrown/likelihood/gauss_family/statistic/source/weak_lensing.py b/firecrown/likelihood/gauss_family/statistic/source/weak_lensing.py index eed27021..ce5758eb 100644 --- a/firecrown/likelihood/gauss_family/statistic/source/weak_lensing.py +++ b/firecrown/likelihood/gauss_family/statistic/source/weak_lensing.py @@ -1,6 +1,4 @@ -"""Weak lensing source and systematics - -""" +"""Weak lensing source and systematics.""" from __future__ import annotations from typing import Optional, final @@ -59,7 +57,7 @@ class PhotoZShift(SourceGalaxyPhotoZShift[WeakLensingArgs]): class SelectField(SourceGalaxySelectField[WeakLensingArgs]): - """Systematic to select 3D field""" + """Systematic to select 3D field.""" class MultiplicativeShearBias(WeakLensingSystematic): @@ -87,15 +85,15 @@ def __init__(self, sacc_tracer: str) -> None: def apply( self, tools: ModelingTools, tracer_arg: WeakLensingArgs ) -> WeakLensingArgs: - """Apply multiplicative shear bias to a source. The `scale_` of the - source is multiplied by `(1 + m)`. + """Apply multiplicative shear bias to a source. + + The `scale_` of the source is multiplied by `(1 + m)`. :param tools: A ModelingTools object. :param tracer_arg: The WeakLensingArgs to which apply the shear bias. :returns: A new WeakLensingArgs object with the shear bias applied. """ - return replace( tracer_arg, scale=tracer_arg.scale * (1.0 + self.mult_bias), @@ -119,8 +117,7 @@ class LinearAlignmentSystematic(WeakLensingSystematic): """ def __init__(self, sacc_tracer: Optional[str] = None, alphag=1.0): - """Create a LinearAlignmentSystematic object, using the specified - tracer name. + """Create a LinearAlignmentSystematic object, using the specified tracer name. :param sacc_tracer: the name of the tracer in the SACC file. This is used as a prefix for its parameters. @@ -136,9 +133,11 @@ def __init__(self, sacc_tracer: Optional[str] = None, alphag=1.0): def apply( self, tools: ModelingTools, tracer_arg: WeakLensingArgs ) -> WeakLensingArgs: - """Return a new linear alignment systematic, based on the given - tracer_arg, in the context of the given cosmology.""" + """Return a new linear alignment systematic. + This choice is based on the given tracer_arg, in the context of the given + cosmology. + """ ccl_cosmo = tools.get_ccl_cosmology() pref = ((1.0 + tracer_arg.z) / (1.0 + self.z_piv)) ** self.alphaz @@ -169,8 +168,7 @@ class TattAlignmentSystematic(WeakLensingSystematic): """ def __init__(self, sacc_tracer: Optional[str] = None): - """Create a TattAlignmentSystematic object, using the specified - tracer name. + """Create a TattAlignmentSystematic object, using the specified tracer name. :param sacc_tracer: the name of the tracer in the SACC file. This is used as a prefix for its parameters. @@ -183,9 +181,11 @@ def __init__(self, sacc_tracer: Optional[str] = None): def apply( self, tools: ModelingTools, tracer_arg: WeakLensingArgs ) -> WeakLensingArgs: - """Return a new linear alignment systematic, based on the given - tracer_arg, in the context of the given cosmology.""" + """Return a new linear alignment systematic. + This choice is based on the given tracer_arg, in the context of the given + cosmology. + """ ccl_cosmo = tools.get_ccl_cosmology() z = tracer_arg.z c_1, c_d, c_2 = pyccl.nl_pt.translate_IA_norm( @@ -237,7 +237,8 @@ def __init__( def _update_source(self, params: ParamsMap): """Implementation of Source interface `_update_source`. - This updates all the contained systematics.""" + This updates all the contained systematics. + """ self.systematics.update(params) def _read(self, sacc_data: sacc.Sacc) -> None: @@ -253,11 +254,7 @@ def _read(self, sacc_data: sacc.Sacc) -> None: super()._read(sacc_data) def create_tracers(self, tools: ModelingTools): - """ - Render a source by applying systematics. - - """ - + """Render a source by applying systematics.""" ccl_cosmo = tools.get_ccl_cosmology() tracer_args = self.tracer_args @@ -296,5 +293,6 @@ def create_tracers(self, tools: ModelingTools): return tracers, tracer_args def get_scale(self): + """Returns the scales for this Source.""" assert self.current_tracer_args return self.current_tracer_args.scale diff --git a/firecrown/likelihood/gauss_family/statistic/statistic.py b/firecrown/likelihood/gauss_family/statistic/statistic.py index 957e12e6..bafdf9c4 100644 --- a/firecrown/likelihood/gauss_family/statistic/statistic.py +++ b/firecrown/likelihood/gauss_family/statistic/statistic.py @@ -1,11 +1,7 @@ -""" - -Gaussian Family Statistic Module -================================ +"""Gaussian Family Statistic Module. The Statistic class describing objects that implement methods to compute the data and theory vectors for a :class:`GaussFamily` subclass. - """ from __future__ import annotations @@ -39,8 +35,7 @@ def from_list(cls, vals: list[float]) -> DataVector: class TheoryVector(npt.NDArray[np.float64]): - """This class wraps a np.ndarray that represents an observation predicted by - some theory.""" + """This class represents an observation predicted by some theory.""" @classmethod def create(cls, vals: npt.NDArray[np.float64]) -> TheoryVector: @@ -56,7 +51,9 @@ def from_list(cls, vals: list[float]) -> TheoryVector: def residuals(data: DataVector, theory: TheoryVector) -> npt.NDArray[np.float64]: """Return a bare np.ndarray with the difference between `data` and `theory`. - This is to be preferred to using arithmetic on the vectors directly.""" + + This is to be preferred to using arithmetic on the vectors directly. + """ assert isinstance(data, DataVector) assert isinstance(theory, TheoryVector) return (data - theory).view(np.ndarray) @@ -78,11 +75,14 @@ def residuals(self) -> npt.NDArray[np.float64]: return self.data - self.theory def __iter__(self): - """Iterate through the data members. This is to allow automatic unpacking, as - if the StatisticsResult were a tuple of (data, theory). + """Iterate through the data members. + + This is to allow automatic unpacking, as if the StatisticsResult were a tuple + of (data, theory). This method is a temporary measure to help code migrate to the newer, - safer interface for Statistic.compute().""" + safer interface for Statistic.compute(). + """ warnings.warn( "Iteration and tuple unpacking for StatisticsResult is " "deprecated.\nPlease use the StatisticsResult class accessors" @@ -93,8 +93,11 @@ def __iter__(self): class StatisticUnreadError(RuntimeError): - """Run-time error indicating an attempt has been made to use a statistic - that has not had `read` called in it.""" + """Error raised when accessing an un-read statistic. + + Run-time error indicating an attempt has been made to use a statistic + that has not had `read` called in it. + """ def __init__(self, stat: Statistic): msg = ( @@ -124,9 +127,10 @@ def __init__(self, parameter_prefix: Optional[str] = None): self.theory_vector: Optional[TheoryVector] = None def read(self, _: sacc.Sacc) -> None: - """Read the data for this statistic from the SACC data, and mark it - as ready for use. Derived classes that override this function - should make sure to call the base class method using: + """Read the data for this statistic and mark it as ready for use. + + Derived classes that override this function should make sure to call the base + class method using: super().read(sacc_data) as the last thing they do in `__init__`. @@ -141,8 +145,10 @@ def read(self, _: sacc.Sacc) -> None: ) def _reset(self): - """Reset this statistic, subclasses implementations must call - super()._reset()""" + """Reset this statistic. + + All subclasses implementations must call super()._reset() + """ self.computed_theory_vector = False self.theory_vector = None @@ -167,9 +173,10 @@ def _compute_theory_vector(self, tools: ModelingTools) -> TheoryVector: """Compute a statistic from sources, concrete implementation.""" def get_theory_vector(self) -> TheoryVector: - """Returns the last computed theory vector. Raises a RuntimeError if the vector - has not been computed.""" + """Returns the last computed theory vector. + Raises a RuntimeError if the vector has not been computed. + """ if not self.computed_theory_vector: raise RuntimeError( f"The theory for statistic {self} has not been computed yet." @@ -182,20 +189,20 @@ def get_theory_vector(self) -> TheoryVector: class GuardedStatistic(Updatable): - """:class:`GuardedStatistic` is used by the framework to maintain and - validate the state of instances of classes derived from - :class:`Statistic`.""" + """An internal class used to maintain state on statistics. + + :class:`GuardedStatistic` is used by the framework to maintain and + validate the state of instances of classes derived from :class:`Statistic`. + """ def __init__(self, stat: Statistic): - """Initialize the GuardedStatistic to contain the given - :class:`Statistic`.""" + """Initialize the GuardedStatistic to contain the given :class:`Statistic`.""" super().__init__() assert isinstance(stat, Statistic) self.statistic = stat def read(self, sacc_data: sacc.Sacc) -> None: - """Read whatever data is needed from the given :class:`sacc.Sacc - object. + """Read whatever data is needed from the given :class:`sacc.Sacc` object. After this function is called, the object should be prepared for the calling of the methods :meth:`get_data_vector` and @@ -217,7 +224,8 @@ def get_data_vector(self) -> DataVector: """Return the contained :class:`Statistic`'s data vector. :class:`GuardedStatistic` ensures that :meth:`read` has been called. - first.""" + first. + """ if not self.statistic.ready: raise StatisticUnreadError(self.statistic) return self.statistic.get_data_vector() @@ -226,7 +234,8 @@ def compute_theory_vector(self, tools: ModelingTools) -> TheoryVector: """Return the contained :class:`Statistic`'s computed theory vector. :class:`GuardedStatistic` ensures that :meth:`read` has been called. - first.""" + first. + """ if not self.statistic.ready: raise StatisticUnreadError(self.statistic) return self.statistic.compute_theory_vector(tools) @@ -251,7 +260,6 @@ def __init__(self) -> None: def read(self, sacc_data: sacc.Sacc): """Read the necessary items from the sacc data.""" - our_data = sacc_data.get_mean(data_type="count") assert len(our_data) == self.count self.data_vector = DataVector.from_list(our_data) diff --git a/firecrown/likelihood/gauss_family/statistic/supernova.py b/firecrown/likelihood/gauss_family/statistic/supernova.py index 63ce8126..04d6f068 100644 --- a/firecrown/likelihood/gauss_family/statistic/supernova.py +++ b/firecrown/likelihood/gauss_family/statistic/supernova.py @@ -1,5 +1,4 @@ -"""Supernova statistic support -""" +"""Supernova statistic support.""" from __future__ import annotations from typing import Optional @@ -17,8 +16,11 @@ class Supernova(Statistic): - """A statistic that applies an additive shift M to a supernova's distance - modulus.""" + """A supernova statistic. + + This statistic that applies an additive shift M to a supernova's distance + modulus. + """ def __init__(self, sacc_tracer: str) -> None: """Initialize this statistic.""" @@ -31,7 +33,6 @@ def __init__(self, sacc_tracer: str) -> None: def read(self, sacc_data: sacc.Sacc) -> None: """Read the data for this statistic from the SACC file.""" - # We do not actually need the tracer, but we want to make sure the SACC # data contains this tracer. # TODO: remove the work-around when the new version of SACC supporting @@ -64,7 +65,6 @@ def get_data_vector(self) -> DataVector: def _compute_theory_vector(self, tools: ModelingTools) -> TheoryVector: """Compute SNIa distance statistic using CCL.""" - ccl_cosmo = tools.get_ccl_cosmology() prediction = self.M + pyccl.distance_modulus(ccl_cosmo, self.a) return TheoryVector.create(prediction) diff --git a/firecrown/likelihood/gauss_family/statistic/two_point.py b/firecrown/likelihood/gauss_family/statistic/two_point.py index ac40a504..525e1d73 100644 --- a/firecrown/likelihood/gauss_family/statistic/two_point.py +++ b/firecrown/likelihood/gauss_family/statistic/two_point.py @@ -1,5 +1,4 @@ -"""Two point statistic support. -""" +"""Two point statistic support.""" from __future__ import annotations @@ -42,13 +41,13 @@ def _ell_for_xi( *, minimum: int, midpoint: int, maximum: int, n_log: int ) -> npt.NDArray[np.float64]: - """Build an array of ells to sample the power spectrum for real-space - predictions. + """Create an array of ells to sample the power spectrum. - The result will contain each integral value from min to mid. - Starting from mid, and going up to max, there will be n_log - logarithmically spaced values. All values are rounded to the nearest - integer. + This is used for for real-space predictions. The result will contain + each integral value from min to mid. Starting from mid, and going up + to max, there will be n_log logarithmically spaced values. + + All values are rounded to the nearest integer. """ assert minimum >= 0 assert minimum < midpoint @@ -114,8 +113,10 @@ def __getitem__(self, item): raise IndexError def __iter__(self): - """Iterate through the data members. This is to allow automatic - unpacking.""" + """Iterate through the data members. + + This is to allow automatic unpacking. + """ yield self.name1 yield self.name2 @@ -126,8 +127,9 @@ def __iter__(self): def read_ell_or_theta_and_stat( ccl_kind: str, sacc_data_type: str, sacc_data: sacc.Sacc, tracers: TracerNames ) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]: - """Read either ell_cl or theta_xi data from sacc_data, and - return that and associated stat data. + """Read and return either ell_cl or theta_xi data and stat. + + These are read from the supplied sacc_data. """ method = sacc_data.get_ell_cl if ccl_kind == "cl" else sacc_data.get_theta_xi ell_or_theta, stat = method(sacc_data_type, *tracers, return_cov=False) @@ -136,8 +138,9 @@ def read_ell_or_theta_and_stat( class TwoPoint(Statistic): - """A two-point statistic (e.g., shear correlation function, galaxy-shear - correlation function, etc.). + """A two-point statistic. + + For example, shear correlation function, galaxy-shear correlation function, etc. Parameters ---------- @@ -271,7 +274,6 @@ def read(self, sacc_data: sacc.Sacc) -> None: :param sacc_data: The data in the sacc format. """ - tracers = self.initialize_sources(sacc_data) _ell_or_theta, _stat = read_ell_or_theta_and_stat( @@ -414,7 +416,6 @@ def get_data_vector(self) -> DataVector: def _compute_theory_vector(self, tools: ModelingTools) -> TheoryVector: """Compute a two-point statistic from sources.""" - assert self._ell_or_theta is not None self.ell_or_theta_ = self._ell_or_theta.copy() diff --git a/firecrown/likelihood/gauss_family/student_t.py b/firecrown/likelihood/gauss_family/student_t.py index 73cdb3fd..127c9f6b 100644 --- a/firecrown/likelihood/gauss_family/student_t.py +++ b/firecrown/likelihood/gauss_family/student_t.py @@ -1,6 +1,4 @@ -"""The Student-t likelihood. - -""" +"""The Student-t likelihood.""" from __future__ import annotations from typing import Optional @@ -14,7 +12,7 @@ class StudentT(GaussFamily): - """A T-distribution for the log-likelihood. + r"""A T-distribution for the log-likelihood. This distribution is appropriate when the covariance has been obtained from a finite number of simulations. See Sellentin & Heavens @@ -22,7 +20,7 @@ class StudentT(GaussFamily): T-distribution approaches a Gaussian. :param statistics: list of statistics to build the theory and data vectors - :param nu: The Student-t $\\nu$ parameter + :param nu: The Student-t $\nu$ parameter """ def __init__( @@ -38,6 +36,5 @@ def compute_loglike(self, tools: ModelingTools): :param cosmo: Current Cosmology object """ - chi2 = self.compute_chisq(tools) return -0.5 * self.nu * np.log(1.0 + chi2 / (self.nu - 1.0)) diff --git a/firecrown/likelihood/likelihood.py b/firecrown/likelihood/likelihood.py index 03a481be..3b30ad17 100644 --- a/firecrown/likelihood/likelihood.py +++ b/firecrown/likelihood/likelihood.py @@ -1,5 +1,4 @@ -"""Basic likelihood infrastructure - +"""Basic likelihood infrastructure. This module provides the base class :class:`Likelihood`, which is the class from which all concrete firecrown likelihoods must descend. @@ -70,9 +69,10 @@ class Likelihood(Updatable): - """Likelihood is an abstract class. Concrete subclasses represent specific - likelihood forms (e.g. gaussian with constant covariance matrix, or Student's t, - etc.). + """Likelihood is an abstract class. + + Concrete subclasses represent specific likelihood forms (e.g. gaussian with + constant covariance matrix, or Student's t, etc.). Concrete subclasses must have an implementation of both *read* and *compute_loglike*. Note that abstract subclasses of Likelihood might implement @@ -88,8 +88,10 @@ def read(self, sacc_data: sacc.Sacc) -> None: """Read the covariance matrix for this likelihood from the SACC file.""" def make_realization_vector(self) -> npt.NDArray[np.float64]: - """Create a new realization of the model using the previously computed - theory vector and covariance matrix. + """Create a new realization of the model. + + This new realization uses the previously computed theory vector and covariance + matrix. """ raise NotImplementedError( "This class does not implement make_realization_vector." @@ -98,8 +100,10 @@ def make_realization_vector(self) -> npt.NDArray[np.float64]: def make_realization( self, sacc_data: sacc.Sacc, add_noise: bool = True, strict: bool = True ) -> sacc.Sacc: - """Create a new realization of the model using the previously computed - theory vector and covariance matrix. + """Create a new realization of the model. + + This realization uses the previously computed theory vector and covariance + matrix. :param sacc_data: The SACC data object containing the covariance matrix :param add_noise: If True, add noise to the realization. If False, return @@ -225,7 +229,6 @@ def set_from_basic_dict( ], ) -> None: """Set the contained data from a dictionary of basic types.""" - for key, value in basic_dict.items(): if isinstance(value, (str, float, int, bool)): self.data = dict(self.data, **{key: value}) @@ -277,7 +280,9 @@ def convert_to_basic_dict( def load_likelihood_from_module_type( module: types.ModuleType, build_parameters: NamedParameters ) -> tuple[Likelihood, ModelingTools]: - """Loads a likelihood and returns a tuple of the likelihood and + """Loads a likelihood from a module type. + + After loading, this method returns a tuple of the likelihood and the modeling tools. This function is used by both :meth:`load_likelihood_from_script` and @@ -288,7 +293,6 @@ def load_likelihood_from_module_type( :param build_parameters: a NamedParameters object containing the factory function parameters """ - if not hasattr(module, "build_likelihood"): if not hasattr(module, "likelihood"): raise AttributeError( @@ -336,7 +340,9 @@ def load_likelihood_from_module_type( def load_likelihood_from_script( filename: str, build_parameters: NamedParameters ) -> tuple[Likelihood, ModelingTools]: - """Loads a likelihood script and returns a tuple of the likelihood and + """Loads a likelihood script. + + After loading, this method returns a tuple of the likelihood and the modeling tools. :param filename: script filename @@ -382,14 +388,15 @@ def load_likelihood_from_script( def load_likelihood_from_module( module: str, build_parameters: NamedParameters ) -> tuple[Likelihood, ModelingTools]: - """Loads a likelihood and returns a tuple of the likelihood and + """Loads a likelihood from a module. + + After loading, this method returns a tuple of the likelihood and the modeling tools. :param module: module name :param build_parameters: a NamedParameters object containing the factory function parameters """ - try: mod = importlib.import_module(module) except ImportError as exc: @@ -403,14 +410,15 @@ def load_likelihood_from_module( def load_likelihood( likelihood_name: str, build_parameters: NamedParameters ) -> tuple[Likelihood, ModelingTools]: - """Loads a likelihood and returns a tuple of the likelihood and + """Loads a likelihood from the provided likelihood_name. + + After loading, this method returns a tuple of the likelihood and the modeling tools. :param likelihood_name: script filename or module name :param build_parameters: a NamedParameters object containing the factory function parameters """ - try: return load_likelihood_from_script(likelihood_name, build_parameters) except ValueError: diff --git a/firecrown/modeling_tools.py b/firecrown/modeling_tools.py index 016bf73f..d2a4b04e 100644 --- a/firecrown/modeling_tools.py +++ b/firecrown/modeling_tools.py @@ -16,8 +16,11 @@ class ModelingTools(Updatable): - """A class that bundles together a :class:`pyccl.Cosmology` object and associated - objects, such as perturbation theory or halo model calculator workspaces.""" + """Modeling tools for likelihoods. + + A class that bundles together a :class:`pyccl.Cosmology` object and associated + objects, such as perturbation theory or halo model calculator workspaces. + """ def __init__( self, @@ -37,16 +40,17 @@ def __init__( def add_pk(self, name: str, powerspectrum: pyccl.Pk2D) -> None: """Add a :python:`pyccl.Pk2D` to the table of power spectra.""" - if name in self.powerspectra: raise KeyError(f"Power spectrum {name} already exists") self.powerspectra[name] = powerspectrum def get_pk(self, name: str) -> pyccl.Pk2D: - """Retrive a pyccl.Pk2D from the table of power spectra, or fall back - to what the pyccl.Cosmology object can provide.""" + """Access a power spectrum from the table of power spectra. + Either retrive a pyccl.Pk2D from the table of power spectra, or fall back + to what the pyccl.Cosmology object can provide. + """ if self.ccl_cosmo is None: raise RuntimeError("Cosmology has not been set") @@ -73,9 +77,7 @@ def prepare(self, ccl_cosmo: pyccl.Cosmology) -> None: if they are needed. :param ccl_cosmo: the current CCL cosmology object - """ - if not self.is_updated(): raise RuntimeError("ModelingTools has not been updated.") @@ -102,8 +104,8 @@ def _reset(self) -> None: This method is called by the Updatable base class when the object is destroyed. It also resets the power spectra, the cosmology and the - _prepared state variable.""" - + _prepared state variable. + """ self.ccl_cosmo = None # Also reset the power spectra # TODO: is that always needed? @@ -112,14 +114,12 @@ def _reset(self) -> None: def get_ccl_cosmology(self) -> pyccl.Cosmology: """Return the CCL cosmology object.""" - if self.ccl_cosmo is None: raise RuntimeError("Cosmology has not been set") return self.ccl_cosmo def get_pt_calculator(self) -> pyccl.nl_pt.EulerianPTCalculator: """Return the perturbation theory calculator object.""" - if self.pt_calculator is None: raise RuntimeError("A PT calculator has not been set") return self.pt_calculator diff --git a/firecrown/models/cluster/__init__.py b/firecrown/models/cluster/__init__.py index e69de29b..16cc2f29 100644 --- a/firecrown/models/cluster/__init__.py +++ b/firecrown/models/cluster/__init__.py @@ -0,0 +1 @@ +"""Module that contains the cluster model classes.""" diff --git a/firecrown/models/cluster/abundance.py b/firecrown/models/cluster/abundance.py index 058ef58a..2cb04607 100644 --- a/firecrown/models/cluster/abundance.py +++ b/firecrown/models/cluster/abundance.py @@ -14,7 +14,7 @@ class ClusterAbundance(Updatable): - """The class that calculates the predicted number counts of galaxy clusters + """The class that calculates the predicted number counts of galaxy clusters. The abundance is a function of a specific cosmology, a mass and redshift range, an area on the sky, a halo mass function, as well as multiple kernels, where @@ -55,7 +55,8 @@ def comoving_volume( ) -> npt.NDArray[np.float64]: """The differential comoving volume given area sky_area at redshift z. - :param sky_area: The area of the survey on the sky in square degrees.""" + :param sky_area: The area of the survey on the sky in square degrees. + """ scale_factor = 1.0 / (1.0 + z) angular_diam_dist = bkg.angular_diameter_distance(self.cosmo, scale_factor) h_over_h0 = bkg.h_over_h0(self.cosmo, scale_factor) diff --git a/firecrown/models/cluster/abundance_data.py b/firecrown/models/cluster/abundance_data.py index c1d9ad31..6baf432d 100644 --- a/firecrown/models/cluster/abundance_data.py +++ b/firecrown/models/cluster/abundance_data.py @@ -1,5 +1,4 @@ -"""The module responsible for extracting cluster data from a sacc file. -""" +"""The module responsible for extracting cluster data from a sacc file.""" import numpy as np import numpy.typing as npt @@ -15,7 +14,8 @@ class AbundanceData: The sacc file is a complicated set of tracers (bins) and surveys. This class manipulates that data and returns only the data relevant for the cluster number count statistic. The data in this class is specific to a single - survey name.""" + survey name. + """ _survey_index = 0 _redshift_index = 1 @@ -43,12 +43,11 @@ def get_observed_data_and_indices_by_survey( survey_nm: str, properties: ClusterProperty, ) -> tuple[list[float], list[int]]: - """Will return observed data (and sacc indices) for a specified survey based on - the properties requested by the caller. + """Returns the observed data for the specified survey and properties. For example if the caller has enabled COUNTS then the observed cluster counts - within each N dimensional bin will be returned.""" - + within each N dimensional bin will be returned. + """ data_vectors = [] sacc_indices = [] diff --git a/firecrown/models/cluster/binning.py b/firecrown/models/cluster/binning.py index 91a9ae35..2dccf8f9 100644 --- a/firecrown/models/cluster/binning.py +++ b/firecrown/models/cluster/binning.py @@ -1,49 +1,41 @@ -"""This module contains the classes that define the bins and binning -used for cluster theoretical predictions within Firecrown.""" +"""Classes for defining bins used in the cluster likelihood. + +This module contains the classes that define the bins and binning +used for cluster theoretical predictions within Firecrown. +""" -from typing import TypeVar, Generic from abc import ABC, abstractmethod import sacc -T = TypeVar("T") - - -class NDimensionalBin(Generic[T], ABC): - """Class which defines the interface for an N dimensional bin used in - the cluster likelihood.""" - def __init__(self, coordinate_bins: list[T]): - """_summary_ - - Args: - coordinate_bins (list[T]): _description_ - dimension (int): _description_ - """ - self.coordinate_bins = coordinate_bins - self.dimension = len(coordinate_bins) +class NDimensionalBin(ABC): + """Class which defines the interface for an N dimensional bin.""" @property @abstractmethod def z_edges(self) -> tuple[float, float]: - """Redshift bin edges""" + """Redshift bin edges.""" @property @abstractmethod def mass_proxy_edges(self) -> tuple[float, float]: - """Mass proxy bin edges""" + """Mass proxy bin edges.""" def __str__(self) -> str: + """Returns a string representation of the bin edges.""" return f"[{self.z_edges}, {self.mass_proxy_edges}]\n" -class SaccBin(NDimensionalBin[sacc.BaseTracer]): +class SaccBin(NDimensionalBin): """An implementation of the N dimensional bin using sacc tracers.""" - def __init__(self, bins: list[sacc.BaseTracer]): - super().__init__(bins) + def __init__(self, coordinate_bins: list[sacc.BaseTracer]): + self.coordinate_bins = coordinate_bins + self.dimension = len(coordinate_bins) @property def z_edges(self) -> tuple[float, float]: + """Redshift bin edges.""" z_bin = [x for x in self.coordinate_bins if x.tracer_type == "bin_z"] if len(z_bin) != 1: raise ValueError("SaccBin must have exactly one z bin") @@ -51,6 +43,7 @@ def z_edges(self) -> tuple[float, float]: @property def mass_proxy_edges(self) -> tuple[float, float]: + """Redshift bin edges.""" mass_bin = [x for x in self.coordinate_bins if x.tracer_type == "bin_richness"] if len(mass_bin) != 1: raise ValueError("SaccBin must have exactly one richness bin") @@ -82,19 +75,22 @@ def __hash__(self) -> int: return hash((self.dimension, tuple(bin_bounds))) -class TupleBin(NDimensionalBin[tuple]): +class TupleBin(NDimensionalBin): """An implementation of the N dimensional bin using sacc tracers.""" - def __init__(self, bins: list[tuple]): - super().__init__(bins) + def __init__(self, coordinate_bins: list[tuple]): + self.coordinate_bins = coordinate_bins + self.dimension = len(coordinate_bins) @property def mass_proxy_edges(self) -> tuple[float, float]: + """Redshift bin edges.""" mass_bin = self.coordinate_bins[0] return mass_bin[0], mass_bin[1] @property def z_edges(self) -> tuple[float, float]: + """Redshift bin edges.""" z_bin = self.coordinate_bins[1] return z_bin[0], z_bin[1] diff --git a/firecrown/models/cluster/integrator/__init__.py b/firecrown/models/cluster/integrator/__init__.py index e69de29b..6363c073 100644 --- a/firecrown/models/cluster/integrator/__init__.py +++ b/firecrown/models/cluster/integrator/__init__.py @@ -0,0 +1 @@ +"""Module for cluster integrator classes.""" diff --git a/firecrown/models/cluster/integrator/integrator.py b/firecrown/models/cluster/integrator/integrator.py index 9cc99c9c..14b1a753 100644 --- a/firecrown/models/cluster/integrator/integrator.py +++ b/firecrown/models/cluster/integrator/integrator.py @@ -1,4 +1,4 @@ -"""The integrator module +"""The cluster integrator module. This module holds the classes that define the interface required to integrate a function. @@ -12,9 +12,10 @@ class Integrator(ABC): - """The integrator base class + """The integrator base class. - This class acts as an adapter around an integration library.""" + This class acts as an adapter around an integration library. + """ def __init__(self) -> None: self.integral_bounds: list[tuple[float, float]] = [] diff --git a/firecrown/models/cluster/integrator/numcosmo_integrator.py b/firecrown/models/cluster/integrator/numcosmo_integrator.py index 2f25afbf..a94d3dcc 100644 --- a/firecrown/models/cluster/integrator/numcosmo_integrator.py +++ b/firecrown/models/cluster/integrator/numcosmo_integrator.py @@ -1,4 +1,4 @@ -"""The NumCosmo integrator module +"""The NumCosmo integrator module. This module holds the NumCosmo implementation of the integrator classes """ @@ -42,6 +42,7 @@ def integrate( [npt.NDArray[np.float64], npt.NDArray[np.float64]], npt.NDArray[np.float64] ], ) -> float: + """Integrate the provided integrand argument with NumCosmo.""" Ncm.cfg_init() int_nd = CountsIntegralND( diff --git a/firecrown/models/cluster/integrator/scipy_integrator.py b/firecrown/models/cluster/integrator/scipy_integrator.py index bc609dd3..433f2918 100644 --- a/firecrown/models/cluster/integrator/scipy_integrator.py +++ b/firecrown/models/cluster/integrator/scipy_integrator.py @@ -1,4 +1,4 @@ -"""The SciPy integrator module +"""The SciPy integrator module. This module holds the scipy implementation of the integrator classes """ @@ -28,6 +28,7 @@ def integrate( [npt.NDArray[np.float64], npt.NDArray[np.float64]], npt.NDArray[np.float64] ], ) -> float: + """Integrate the provided integrand argument with SciPy.""" val = nquad( func_to_integrate, ranges=self.integral_bounds, diff --git a/firecrown/models/cluster/kernel.py b/firecrown/models/cluster/kernel.py index a64eb667..7cf91349 100644 --- a/firecrown/models/cluster/kernel.py +++ b/firecrown/models/cluster/kernel.py @@ -1,7 +1,8 @@ -"""The cluster kernel module +"""The cluster kernel module. This module holds the classes that define the kernels that can be included -in the cluster abundance integrand.""" +in the cluster abundance integrand. +""" from enum import Enum import numpy.typing as npt @@ -9,7 +10,7 @@ class KernelType(Enum): - """The kernels that can be included in the cluster abundance integrand""" + """The kernels that can be included in the cluster abundance integrand.""" MASS = 1 Z = 2 @@ -19,12 +20,12 @@ class KernelType(Enum): PURITY = 6 -# pylint: disable=too-few-public-methods class Completeness: - """The completeness kernel for the numcosmo simulated survey + """The completeness kernel for the numcosmo simulated survey. This kernel will affect the integrand by accounting for the incompleteness - of a cluster selection.""" + of a cluster selection. + """ def distribution( self, @@ -43,12 +44,12 @@ def distribution( return completeness -# pylint: disable=too-few-public-methods class Purity: - """The purity kernel for the numcosmo simulated survey + """The purity kernel for the numcosmo simulated survey. This kernel will affect the integrand by accounting for the purity - of a cluster selection.""" + of a cluster selection. + """ def _ln_rc(self, z: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]: a_rc = 2.2183 @@ -83,26 +84,30 @@ def distribution( return purity -# pylint: disable=too-few-public-methods class TrueMass: """The true mass kernel. - Assuming we measure the true mass, this will always be 1.""" + Assuming we measure the true mass, this will always be 1. + """ def distribution(self) -> npt.NDArray[np.float64]: """Evaluates and returns the mass distribution contribution to the integrand. - We have set this to 1.0 (i.e. it does not affect the mass distribution)""" + + We have set this to 1.0 (i.e. it does not affect the mass distribution) + """ return np.atleast_1d(1.0) -# pylint: disable=too-few-public-methods class SpectroscopicRedshift: """The spec-z kernel. Assuming the spectroscopic redshift has no uncertainties, this is akin to - multiplying by 1.""" + multiplying by 1. + """ def distribution(self) -> npt.NDArray[np.float64]: """Evaluates and returns the z distribution contribution to the integrand. - We have set this to 1.0 (i.e. it does not affect the redshift distribution)""" + + We have set this to 1.0 (i.e. it does not affect the redshift distribution) + """ return np.atleast_1d(1.0) diff --git a/firecrown/models/cluster/mass_proxy.py b/firecrown/models/cluster/mass_proxy.py index 3a53601b..71ec0b8d 100644 --- a/firecrown/models/cluster/mass_proxy.py +++ b/firecrown/models/cluster/mass_proxy.py @@ -1,8 +1,9 @@ -"""The mass richness kernel module +"""The mass richness kernel module. This module holds the classes that define the mass richness relations that can be included in the cluster abundance integrand. These are -implementations of Kernels.""" +implementations of Kernels. +""" from abc import abstractmethod @@ -26,7 +27,6 @@ def observed_value( log1p_pivot_redshift: float, ) -> npt.NDArray[np.float64]: """Return observed quantity corrected by redshift and mass.""" - ln_mass = mass * np.log(10) delta_ln_mass = ln_mass - pivot_mass delta_z = np.log1p(z) - log1p_pivot_redshift @@ -128,6 +128,7 @@ def get_proxy_mean( mass: npt.NDArray[np.float64], z: npt.NDArray[np.float64], ) -> npt.NDArray[np.float64]: + """Return observed quantity corrected by redshift and mass.""" return MassRichnessGaussian.observed_value( (self.mu_p0, self.mu_p1, self.mu_p2), mass, @@ -141,6 +142,7 @@ def get_proxy_sigma( mass: npt.NDArray[np.float64], z: npt.NDArray[np.float64], ) -> npt.NDArray[np.float64]: + """Return observed scatter corrected by redshift and mass.""" return MassRichnessGaussian.observed_value( (self.sigma_p0, self.sigma_p1, self.sigma_p2), mass, @@ -185,6 +187,7 @@ def get_proxy_mean( mass: npt.NDArray[np.float64], z: npt.NDArray[np.float64], ) -> npt.NDArray[np.float64]: + """Return observed quantity corrected by redshift and mass.""" return MassRichnessGaussian.observed_value( (self.mu_p0, self.mu_p1, self.mu_p2), mass, @@ -198,6 +201,7 @@ def get_proxy_sigma( mass: npt.NDArray[np.float64], z: npt.NDArray[np.float64], ) -> npt.NDArray[np.float64]: + """Return observed scatter corrected by redshift and mass.""" return MassRichnessGaussian.observed_value( (self.sigma_p0, self.sigma_p1, self.sigma_p2), mass, diff --git a/firecrown/models/cluster/properties.py b/firecrown/models/cluster/properties.py index 3e6fa8bf..0746cd61 100644 --- a/firecrown/models/cluster/properties.py +++ b/firecrown/models/cluster/properties.py @@ -4,8 +4,11 @@ class ClusterProperty(Flag): - """Flag containing the possible cluster properties we can make a theoretical - prediction for.""" + """Flag representing cluster observables. + + This flag containing the possible cluster properties we can make a theoretical + prediction for. + """ NONE = 0 COUNTS = auto() diff --git a/firecrown/models/cluster/recipes/__init__.py b/firecrown/models/cluster/recipes/__init__.py index e69de29b..a33a6a8f 100644 --- a/firecrown/models/cluster/recipes/__init__.py +++ b/firecrown/models/cluster/recipes/__init__.py @@ -0,0 +1 @@ +"""Module for cluster recipe classes.""" diff --git a/firecrown/models/cluster/recipes/cluster_recipe.py b/firecrown/models/cluster/recipes/cluster_recipe.py index 29f09b1e..0c6ff96d 100644 --- a/firecrown/models/cluster/recipes/cluster_recipe.py +++ b/firecrown/models/cluster/recipes/cluster_recipe.py @@ -1,4 +1,4 @@ -"""Module for defining the ClusterRecipe class""" +"""Module for defining the ClusterRecipe class.""" from abc import ABC, abstractmethod from typing import Optional @@ -13,7 +13,8 @@ class ClusterRecipe(Updatable, ABC): """Abstract class defining a cluster recipe. A cluster recipe is a combination of different cluster theoretrical predictions - and models that produces a single prediction for an observable.""" + and models that produces a single prediction for an observable. + """ def __init__(self, parameter_prefix: Optional[str] = None) -> None: super().__init__(parameter_prefix) @@ -27,4 +28,4 @@ def evaluate_theory_prediction( sky_area: float, average_on: Optional[ClusterProperty] = None, ) -> float: - """Evaluate the theory prediction for this cluster recipe""" + """Evaluate the theory prediction for this cluster recipe.""" diff --git a/firecrown/models/cluster/recipes/murata_binned_spec_z.py b/firecrown/models/cluster/recipes/murata_binned_spec_z.py index 64ab9d49..61411889 100644 --- a/firecrown/models/cluster/recipes/murata_binned_spec_z.py +++ b/firecrown/models/cluster/recipes/murata_binned_spec_z.py @@ -15,8 +15,11 @@ class MurataBinnedSpecZRecipe(ClusterRecipe): - """Cluster recipe using the Murata 2019 binned mass-richness relation and assuming - perfectly measured spec-zs.""" + """Cluster recipe with Murata19 mass-richness and spec-zs. + + This recipe uses the Murata 2019 binned mass-richness relation and assumes + perfectly measured spec-zs. + """ def __init__(self) -> None: super().__init__() @@ -35,9 +38,12 @@ def get_theory_prediction( [npt.NDArray[np.float64], npt.NDArray[np.float64], tuple[float, float], float], npt.NDArray[np.float64], ]: - """Returns a callable function that accepts mass, redshift, mass proxy limits, + """Get a callable that evaluates a cluster theory prediction. + + Returns a callable function that accepts mass, redshift, mass proxy limits, and the sky area of your survey and returns the theoretical prediction for the - expected number of clusters.""" + expected number of clusters. + """ def theory_prediction( mass: npt.NDArray[np.float64], @@ -85,7 +91,8 @@ def get_function_to_integrate( """Returns a callable function that can be evaluated by an integrator. This function is responsible for mapping arguments from the numerical integrator - to the arguments of the theoretical prediction function.""" + to the arguments of the theoretical prediction function. + """ def function_mapper( int_args: npt.NDArray, extra_args: npt.NDArray @@ -108,9 +115,12 @@ def evaluate_theory_prediction( sky_area: float, average_on: Optional[ClusterProperty] = None, ) -> float: - """Evaluate the theoretical prediction for the observable in the provided bin + """Evaluate the theory prediction for this cluster recipe. + + Evaluate the theoretical prediction for the observable in the provided bin using the Murata 2019 binned mass-richness relation and assuming perfectly - measured redshifts.""" + measured redshifts. + """ self.integrator.integral_bounds = [ (cluster_theory.min_mass, cluster_theory.max_mass), this_bin.z_edges, diff --git a/firecrown/models/cluster/recipes/murata_unbinned_spec_z.py b/firecrown/models/cluster/recipes/murata_unbinned_spec_z.py deleted file mode 100644 index f1a82d04..00000000 --- a/firecrown/models/cluster/recipes/murata_unbinned_spec_z.py +++ /dev/null @@ -1,131 +0,0 @@ -"""Module for defining the classes used in the MurataUnbinnedSpecZ cluster recipe.""" - -from typing import Callable, Optional - -import numpy as np -import numpy.typing as npt - -from firecrown.models.cluster.abundance import ClusterAbundance -from firecrown.models.cluster.binning import NDimensionalBin -from firecrown.models.cluster.integrator.numcosmo_integrator import NumCosmoIntegrator -from firecrown.models.cluster.kernel import SpectroscopicRedshift -from firecrown.models.cluster.mass_proxy import MurataUnbinned -from firecrown.models.cluster.properties import ClusterProperty -from firecrown.models.cluster.recipes.cluster_recipe import ClusterRecipe - - -class MurataUnbinnedSpecZRecipe(ClusterRecipe): - """Cluster recipe using the Murata 2019 unbinned mass-richness relation and assuming - perfectly measured spec-zs.""" - - def __init__(self) -> None: - super().__init__() - - self.integrator = NumCosmoIntegrator() - self.redshift_distribution = SpectroscopicRedshift() - pivot_mass, pivot_redshift = 14.625862906, 0.6 - self.mass_distribution = MurataUnbinned(pivot_mass, pivot_redshift) - self.my_updatables.append(self.mass_distribution) - - def get_theory_prediction( - self, - cluster_theory: ClusterAbundance, - average_on: Optional[ClusterProperty] = None, - ) -> Callable[ - [ - npt.NDArray[np.float64], - npt.NDArray[np.float64], - npt.NDArray[np.float64], - float, - ], - npt.NDArray[np.float64], - ]: - """Returns a callable function that accepts mass, redshift, mass proxy, - and the sky area of your survey and returns the theoretical prediction for the - expected number of clusters.""" - - def theory_prediction( - mass: npt.NDArray[np.float64], - z: npt.NDArray[np.float64], - mass_proxy: npt.NDArray[np.float64], - sky_area: float, - ): - prediction = ( - cluster_theory.comoving_volume(z, sky_area) - * cluster_theory.mass_function(mass, z) - * self.redshift_distribution.distribution() - * self.mass_distribution.distribution(mass, z, mass_proxy) - ) - - if average_on is None: - return prediction - - for cluster_prop in ClusterProperty: - include_prop = cluster_prop & average_on - if not include_prop: - continue - if cluster_prop == ClusterProperty.MASS: - prediction *= mass - elif cluster_prop == ClusterProperty.REDSHIFT: - prediction *= z - else: - raise NotImplementedError(f"Average for {cluster_prop}.") - - return prediction - - return theory_prediction - - def get_function_to_integrate( - self, - prediction: Callable[ - [ - npt.NDArray[np.float64], - npt.NDArray[np.float64], - npt.NDArray[np.float64], - float, - ], - npt.NDArray[np.float64], - ], - ) -> Callable[ - [npt.NDArray[np.float64], npt.NDArray[np.float64]], npt.NDArray[np.float64] - ]: - """Returns a callable function that can be evaluated by an integrator. - - This function is responsible for mapping arguments from the numerical integrator - to the arguments of the theoretical prediction function.""" - - def function_mapper( - int_args: npt.NDArray[np.float64], extra_args: npt.NDArray[np.float64] - ) -> npt.NDArray[np.float64]: - mass = int_args[:, 0] - z = int_args[:, 1] - mass_proxy = int_args[:, 2] - sky_area = extra_args[0] - - return prediction(mass, z, mass_proxy, sky_area) - - return function_mapper - - def evaluate_theory_prediction( - self, - cluster_theory: ClusterAbundance, - this_bin: NDimensionalBin, - sky_area: float, - average_on: Optional[ClusterProperty] = None, - ) -> float: - """Evaluate the theoretical prediction for the observable in the provided bin - using the Murata 2019 unbinned mass-richness relation and assuming perfectly - measured redshifts.""" - self.integrator.integral_bounds = [ - (cluster_theory.min_mass, cluster_theory.max_mass), - this_bin.z_edges, - this_bin.mass_proxy_edges, - ] - self.integrator.extra_args = np.array([sky_area]) - - theory_prediction = self.get_theory_prediction(cluster_theory, average_on) - prediction_wrapper = self.get_function_to_integrate(theory_prediction) - - counts = self.integrator.integrate(prediction_wrapper) - - return counts diff --git a/firecrown/parameters.py b/firecrown/parameters.py index 302c0eb0..2c19d796 100644 --- a/firecrown/parameters.py +++ b/firecrown/parameters.py @@ -1,6 +1,4 @@ -"""Classes and functions to support groups of named parameters. - -""" +"""Classes and functions to support groups of named parameters.""" from __future__ import annotations from typing import Iterable, Optional, Iterator, Sequence @@ -43,6 +41,7 @@ def __init__(self, *args, **kwargs) -> None: def use_lower_case_keys(self, enable: bool) -> None: """Control whether keys will be translated into lower case. + If `enable` is True, such translation will be done. This can help make sure code works with CosmoSIS, because such translation is done inside CosmoSIS itself. @@ -54,7 +53,6 @@ def use_lower_case_keys(self, enable: bool) -> None: def get_from_prefix_param(self, prefix: Optional[str], param: str) -> float: """Return the parameter identified by the optional prefix and parameter name. - See parameter_get_full_name for rules on the forming of prefix and name. Raises a KeyError if the parameter is not found. """ @@ -94,7 +92,8 @@ def __add__(self, other: RequiredParameters) -> RequiredParameters: """Return a new RequiredParameters with the concatenated names. Note that this function returns a new object that does not share state - with either argument to the addition operator.""" + with either argument to the addition operator. + """ return RequiredParameters(self.params_names | other.params_names) def __eq__(self, other: object): @@ -120,7 +119,7 @@ def get_params_names(self) -> Iterator[str]: class DerivedParameter: - """Represents a derived scalar parameter generated by an Updatable object + """Represents a derived scalar parameter generated by an Updatable object. This class provide the type that encapsulate a derived scalar quantity (represented by a float) computed by an Updatable object during a statistical analysis. @@ -170,7 +169,6 @@ class DerivedParameterCollection: def __init__(self, derived_parameters: Sequence[DerivedParameter]): """Construct an instance from a sequence of DerivedParameter objects.""" - if not all(isinstance(x, DerivedParameter) for x in derived_parameters): raise TypeError( "DerivedParameterCollection expects a list of DerivedParameter" @@ -183,14 +181,17 @@ def __init__(self, derived_parameters: Sequence[DerivedParameter]): self.add_required_parameter(derived_parameter) def __add__(self, other: Optional[DerivedParameterCollection]): - """Return a new DerivedParameterCollection with the lists of DerivedParameter + """Add two DerivedParameterCollection objects. + + Return a new DerivedParameterCollection with the lists of DerivedParameter objects. If other is none return self. Otherwise, constructs a new object representing the addition. Note that this function returns a new object that does not share state - with either argument to the addition operator.""" + with either argument to the addition operator. + """ if other is None: return self @@ -208,7 +209,6 @@ def __eq__(self, other: object): Two DerivedParameterCollection objects are equal if they contain the same DerivedParameter objects. """ - if not isinstance(other, DerivedParameterCollection): raise NotImplementedError( "DerivedParameterCollection comparison is only implemented for " @@ -217,6 +217,7 @@ def __eq__(self, other: object): return self.derived_parameters == other.derived_parameters def __iter__(self) -> Iterator[tuple[str, str, float]]: + """Implementation of lazy iteration through the collection.""" for derived_parameter in self.derived_parameters.values(): yield ( derived_parameter.section, @@ -225,10 +226,11 @@ def __iter__(self) -> Iterator[tuple[str, str, float]]: ) def add_required_parameter(self, derived_parameter: DerivedParameter): - """Adds derived_parameter to the collection, it raises an ValueError if a - required parameter with the same name is already present in the collection. - """ + """Adds derived_parameter to the collection. + We raises an ValueError if a required parameter with the same name is already + present in the collection. + """ required_parameter_full_name = derived_parameter.get_full_name() if required_parameter_full_name in self.derived_parameters: raise ValueError( @@ -239,7 +241,6 @@ def add_required_parameter(self, derived_parameter: DerivedParameter): def get_derived_list(self) -> list[DerivedParameter]: """Implement lazy iteration through the contained parameter names.""" - return list(self.derived_parameters.values()) @@ -247,8 +248,10 @@ class SamplerParameter: """Class to represent a sampler defined parameter.""" def __init__(self): - """Creates a new SamplerParameter instance that represents a parameter - having its value defined by the sampler.""" + """Creates a new SamplerParameter instance. + + This represents a parameter having its value defined by the sampler. + """ self.value: Optional[float] = None def set_value(self, value: float): @@ -268,8 +271,10 @@ class InternalParameter: """Class to represent an internally defined parameter.""" def __init__(self, value: float): - """Creates a new InternalParameter instance that represents an - internal parameter with its value defined by value.""" + """Creates a new InternalParameter instance. + + This represents an internal parameter with its value defined by value. + """ self.value = value def set_value(self, value: float): @@ -289,7 +294,8 @@ def get_value(self) -> float: def create(value: Optional[float] = None): """Create a new parameter, either a SamplerParameter or an InternalParameter. - See register_new_updatable_parameter for details.""" + See register_new_updatable_parameter for details. + """ warnings.simplefilter("always", DeprecationWarning) warnings.warn( "This function is named `create` and will be removed in a future version " diff --git a/firecrown/updatable.py b/firecrown/updatable.py index 49e10a60..c2f4f0f6 100644 --- a/firecrown/updatable.py +++ b/firecrown/updatable.py @@ -39,9 +39,11 @@ class MissingSamplerParameterError(RuntimeError): - """Error class raised when an Updatable failes to be updated because the - ParamsMap supplied for the update is missing a parameter that should have - been provided by the sampler.""" + """Error for when a required parameter is missing. + + Raised when an Updatable fails to be updated because the ParamsMap supplied for the + update is missing a parameter that should have been provided by the sampler. + """ def __init__(self, parameter: str): """Create the error, with a meaning error message.""" @@ -102,9 +104,11 @@ def __setattr__(self, key: str, value: Any) -> None: def set_parameter( self, key: str, value: Union[InternalParameter, SamplerParameter] ) -> None: - """Assure this InternalParameter or SamplerParameter has not already - been set, and then set it.""" + """Sets the parameter to the given value. + Assure this InternalParameter or SamplerParameter has not already + been set, and then set it. + """ if isinstance(value, SamplerParameter): self.set_sampler_parameter(key, value) elif isinstance(value, InternalParameter): @@ -112,7 +116,6 @@ def set_parameter( def set_internal_parameter(self, key: str, value: InternalParameter) -> None: """Assure this InternalParameter has not already been set, and then set it.""" - if not isinstance(value, InternalParameter): raise TypeError( "Can only add InternalParameter objects to internal_parameters" @@ -128,7 +131,6 @@ def set_internal_parameter(self, key: str, value: InternalParameter) -> None: def set_sampler_parameter(self, key: str, value: SamplerParameter) -> None: """Assure this SamplerParameter has not already been set, and then set it.""" - if not isinstance(value, SamplerParameter): raise TypeError( "Can only add SamplerParameter objects to sampler_parameters" @@ -186,21 +188,26 @@ def update(self, params: ParamsMap) -> None: self._updated = True def is_updated(self) -> bool: - """Return True if the object is currently updated, and False if not. + """Determine if the object has been updated. + + Return True if the object is currently updated, and False if not. A default-constructed Updatable has not been updated. After `update`, but before `reset`, has been called the object is updated. After - `reset` has been called, the object is not currently updated.""" + `reset` has been called, the object is not currently updated. + """ return self._updated @final def reset(self) -> None: - """Clean up self by clearing the _updated status and reseting all + """Reset the updatable. + + Clean up self by clearing the _updated status and reseting all internals. We call the abstract method _reset to allow derived classes to clean up any additional internals. Each MCMC framework connector should call this after handling an MCMC - sample.""" - + sample. + """ # If we have not been updated, there is nothing to do. if not self._updated: return @@ -219,12 +226,12 @@ def reset(self) -> None: self._reset() def _update(self, params: ParamsMap) -> None: - """Do any updating other than calling :meth:`update` on contained - :class:`Updatable` objects. + """Method for auxiliary updates to be made to an updatable. - Implement this method in a subclass only when it has something to do. - If the supplied :class:`ParamsMap` is lacking a required parameter, - an implementation should raise a `TypeError`. + Do any updating other than calling :meth:`update` on contained + :class:`Updatable` objects. Implement this method in a subclass only when it + has something to do. If the supplied :class:`ParamsMap` is lacking a required + parameter, an implementation should raise a `TypeError`. This default implementation does nothing. @@ -232,8 +239,7 @@ def _update(self, params: ParamsMap) -> None: """ def _reset(self) -> None: # pragma: no cover - """Abstract method to be implemented by all concrete classes to update - self. + """Abstract method implemented by all concrete classes to update self. Concrete classes must override this, resetting themselves. @@ -242,11 +248,11 @@ def _reset(self) -> None: # pragma: no cover @final def required_parameters(self) -> RequiredParameters: # pragma: no cover - """Return a RequiredParameters object containing the information for - all parameters defined in the implementing class, any additional - parameter. - """ + """Returns a RequiredParameters object. + This object contains the information for all parameters defined in the + implementing class, any additional parameter. + """ sampler_parameters = RequiredParameters( [ parameter_get_full_name(self.parameter_prefix, parameter) @@ -261,26 +267,26 @@ def required_parameters(self) -> RequiredParameters: # pragma: no cover return sampler_parameters + additional_parameters def _required_parameters(self) -> RequiredParameters: # pragma: no cover - """Return a RequiredParameters object containing the information for - this Updatable. This method can be overridden by subclasses to add + """Return a RequiredParameters object containing the information for this class. + + This method can be overridden by subclasses to add additional parameters. The default implementation returns an empty RequiredParameters object. This is only implemented to allow The base class implementation returns a list with all SamplerParameter objects properties. """ - return RequiredParameters([]) @final def get_derived_parameters( self, ) -> Optional[DerivedParameterCollection]: - """Returns a collection of derived parameters once per iteration of the - statistical analysis. First call returns the DerivedParameterCollection, - further calls return None. - """ + """Returns a collection of derived parameters. + This occurs once per iteration of the statistical analysis. First call returns + the DerivedParameterCollection, further calls return None. + """ if not self._updated: raise RuntimeError( "Derived parameters can only be obtained after update has been called." @@ -298,8 +304,7 @@ def get_derived_parameters( return derived_parameters def _get_derived_parameters(self) -> DerivedParameterCollection: - """Abstract method to be implemented by all concrete classes to return their - derived parameters. + """Returns the derived parameters of an implementation. Derived classes can override this, returning a DerivedParameterCollection containing the derived parameters for the class. The default implementation @@ -312,7 +317,9 @@ def _get_derived_parameters(self) -> DerivedParameterCollection: class UpdatableCollection(UserList[T], Generic[T]): - """UpdatableCollection is a list of Updatable objects and is itself + """Class that represents a collection of updatable objects. + + UpdatableCollection is a list of Updatable objects and is itself supports :meth:`update` and :meth:`reset` (although it does not inherit from :class:`Updatable`). @@ -352,10 +359,13 @@ def update(self, params: ParamsMap) -> None: self._updated = True def is_updated(self) -> bool: - """Return True if the object is currently updated, and False if not. + """Returns whether this updatable has been updated. + + Return True if the object is currently updated, and False if not. A default-constructed Updatable has not been updated. After `update`, but before `reset`, has been called the object is updated. After - `reset` has been called, the object is not currently updated.""" + `reset` has been called, the object is not currently updated. + """ return self._updated @final @@ -367,7 +377,9 @@ def reset(self) -> None: @final def required_parameters(self) -> RequiredParameters: - """Return a RequiredParameters object formed by concatenating the + """Return a RequiredParameters object. + + The RequiredParameters object is formed by concatenating the RequiredParameters of each contained item. """ result = RequiredParameters([]) diff --git a/firecrown/utils.py b/firecrown/utils.py index 7937f514..bd1176cd 100644 --- a/firecrown/utils.py +++ b/firecrown/utils.py @@ -1,5 +1,4 @@ -"""Some utility functions for patterns common in Firecrown. -""" +"""Some utility functions for patterns common in Firecrown.""" from __future__ import annotations @@ -10,7 +9,9 @@ def upper_triangle_indices(n: int): - """generator that yields a sequence of tuples that carry the indices for an + """Returns the upper triangular indices for an (n x n) matrix. + + generator that yields a sequence of tuples that carry the indices for an (n x n) upper-triangular matrix. This is a replacement for the nested loops: for i in range(n): @@ -51,7 +52,6 @@ def save_to_sacc( new_sacc: sacc.Sacc A copy of `sacc_data`, with data at `indices` replaced with `data_vector`. """ - assert len(indices) == len(data_vector) new_sacc = sacc_data.copy() diff --git a/firecrown/version.py b/firecrown/version.py index e80002db..4f1b30c0 100644 --- a/firecrown/version.py +++ b/firecrown/version.py @@ -1,5 +1,5 @@ -"""Version data for Firecrown. - +""" +Version data for Firecrown. The versioning of Firecrown is intended to follow *Semantic Versioning*, version 2. diff --git a/setup.cfg b/setup.cfg index b80aca53..3054d675 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,8 +35,10 @@ exclude = [flake8] max_complexity=10 max_line_length=88 +ignore=D107, D401, D402, W503 per-file-ignores = examples/srd_sn/generate_sn_data.py:E501 + tests/*.py:D100,D102,D103,D104,D200,D202,D205,D209,D400 [mypy] ignore_missing_imports = True diff --git a/tests/cluster_recipes/test_murata_unbinned_spec_z.py b/tests/cluster_recipes/test_murata_unbinned_spec_z.py deleted file mode 100644 index 938001fa..00000000 --- a/tests/cluster_recipes/test_murata_unbinned_spec_z.py +++ /dev/null @@ -1,185 +0,0 @@ -"""Tests for the cluster abundance module.""" - -from unittest.mock import Mock - -import numpy as np -import pyccl -import pytest - -from firecrown.models.cluster.abundance import ClusterAbundance -from firecrown.models.cluster.binning import NDimensionalBin -from firecrown.models.cluster.integrator.numcosmo_integrator import NumCosmoIntegrator -from firecrown.models.cluster.kernel import SpectroscopicRedshift -from firecrown.models.cluster.mass_proxy import MurataUnbinned -from firecrown.models.cluster.properties import ClusterProperty -from firecrown.models.cluster.recipes.cluster_recipe import ClusterRecipe -from firecrown.models.cluster.recipes.murata_unbinned_spec_z import ( - MurataUnbinnedSpecZRecipe, -) - - -@pytest.fixture(name="cluster_abundance") -def fixture_cluster_abundance() -> ClusterAbundance: - hmf = pyccl.halos.MassFuncBocquet16() - cl_abundance = ClusterAbundance( - min_z=0, - max_z=2, - min_mass=13, - max_mass=17, - halo_mass_function=hmf, - ) - cl_abundance.update_ingredients(pyccl.CosmologyVanillaLCDM()) - return cl_abundance - - -@pytest.fixture(name="murata_unbinned_spec_z") -def fixture_murata_unbinned_spec_z() -> MurataUnbinnedSpecZRecipe: - cluster_recipe = MurataUnbinnedSpecZRecipe() - cluster_recipe.mass_distribution.mu_p0 = 3.0 - cluster_recipe.mass_distribution.mu_p1 = 0.86 - cluster_recipe.mass_distribution.mu_p2 = 0.0 - cluster_recipe.mass_distribution.sigma_p0 = 3.0 - cluster_recipe.mass_distribution.sigma_p1 = 0.7 - cluster_recipe.mass_distribution.sigma_p2 = 0.0 - return cluster_recipe - - -def test_murata_unbinned_spec_z_init(): - recipe = MurataUnbinnedSpecZRecipe() - - assert recipe is not None - assert isinstance(recipe, ClusterRecipe) - assert recipe.integrator is not None - assert isinstance(recipe.integrator, NumCosmoIntegrator) - assert recipe.redshift_distribution is not None - assert isinstance(recipe.redshift_distribution, SpectroscopicRedshift) - assert recipe.mass_distribution is not None - assert isinstance(recipe.mass_distribution, MurataUnbinned) - assert recipe.my_updatables is not None - assert len(recipe.my_updatables) == 1 - assert recipe.my_updatables[0] is recipe.mass_distribution - - -def test_get_theory_prediction_returns_value( - cluster_abundance: ClusterAbundance, - murata_unbinned_spec_z: MurataUnbinnedSpecZRecipe, -): - prediction = murata_unbinned_spec_z.get_theory_prediction(cluster_abundance) - - assert prediction is not None - assert callable(prediction) - - mass = np.linspace(13, 17, 2) - z = np.linspace(0.1, 1, 2) - mass_proxy = np.linspace(0, 5, 2) - sky_area = 360**2 - - result = prediction(mass, z, mass_proxy, sky_area) - assert isinstance(result, np.ndarray) - assert np.issubdtype(result.dtype, np.float64) - assert len(result) == 2 - assert np.all(result > 0) - - -def test_get_theory_prediction_with_average_returns_value( - cluster_abundance: ClusterAbundance, - murata_unbinned_spec_z: MurataUnbinnedSpecZRecipe, -): - mass = np.linspace(13, 17, 2) - z = np.linspace(0.1, 1, 2) - mass_proxy = np.linspace(0, 5, 2) - sky_area = 360**2 - - prediction = murata_unbinned_spec_z.get_theory_prediction( - cluster_abundance, average_on=ClusterProperty.MASS - ) - - assert prediction is not None - assert callable(prediction) - - result = prediction(mass, z, mass_proxy, sky_area) - assert isinstance(result, np.ndarray) - assert np.issubdtype(result.dtype, np.float64) - assert len(result) == 2 - assert np.all(result > 0) - - prediction = murata_unbinned_spec_z.get_theory_prediction( - cluster_abundance, average_on=ClusterProperty.REDSHIFT - ) - - assert prediction is not None - assert callable(prediction) - - result = prediction(mass, z, mass_proxy, sky_area) - assert isinstance(result, np.ndarray) - assert np.issubdtype(result.dtype, np.float64) - assert len(result) == 2 - assert np.all(result > 0) - - prediction = murata_unbinned_spec_z.get_theory_prediction( - cluster_abundance, average_on=(ClusterProperty.REDSHIFT | ClusterProperty.MASS) - ) - - assert prediction is not None - assert callable(prediction) - - result = prediction(mass, z, mass_proxy, sky_area) - assert isinstance(result, np.ndarray) - assert np.issubdtype(result.dtype, np.float64) - assert len(result) == 2 - assert np.all(result > 0) - - -def test_get_theory_prediction_throws_with_nonimpl_average( - cluster_abundance: ClusterAbundance, - murata_unbinned_spec_z: MurataUnbinnedSpecZRecipe, -): - prediction = murata_unbinned_spec_z.get_theory_prediction( - cluster_abundance, average_on=ClusterProperty.SHEAR - ) - - assert prediction is not None - assert callable(prediction) - - mass = np.linspace(13, 17, 2) - z = np.linspace(0.1, 1, 2) - mass_proxy = np.linspace(0, 5, 2) - sky_area = 360**2 - - with pytest.raises(NotImplementedError): - _ = prediction(mass, z, mass_proxy, sky_area) - - -def test_get_function_to_integrate_returns_value( - cluster_abundance: ClusterAbundance, - murata_unbinned_spec_z: MurataUnbinnedSpecZRecipe, -): - prediction = murata_unbinned_spec_z.get_theory_prediction(cluster_abundance) - function_to_integrate = murata_unbinned_spec_z.get_function_to_integrate(prediction) - - assert function_to_integrate is not None - assert callable(function_to_integrate) - - int_args = np.array([[13.0, 0.1, 0.1], [17.0, 1.0, 4.9]]) - extra_args = np.array([360**2]) - - result = function_to_integrate(int_args, extra_args) - assert isinstance(result, np.ndarray) - assert np.issubdtype(result.dtype, np.float64) - assert len(result) == 2 - assert np.all(result > 0) - - -def test_evaluates_theory_prediction_returns_value( - cluster_abundance: ClusterAbundance, - murata_unbinned_spec_z: MurataUnbinnedSpecZRecipe, -): - mock_bin = Mock(spec=NDimensionalBin) - mock_bin.mass_proxy_edges = (0, 5) - mock_bin.z_edges = (0, 1) - - prediction = murata_unbinned_spec_z.evaluate_theory_prediction( - cluster_abundance, mock_bin, 360**2 - ) - - assert prediction > 0