:py:mod:`profiley.core` ======================= .. py:module:: profiley.core Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: profiley.core.Profile Functions ~~~~~~~~~ .. autoapisummary:: profiley.core.binned Attributes ~~~~~~~~~~ .. autoapisummary:: profiley.core.has_ccl profiley.core.G .. py:data:: has_ccl :value: True .. py:data:: G .. py:function:: binned(func, r, rbins, *args, **kwargs) Return binned profile measurements Parameters ---------- func : callable The function to be binned r : np.ndarray, shape (M,) Locations to calculate the unbinned profile. This array should be fine enough to ensure binned profile is accurate rbins : np.ndarray, shape (N,) bins applied to profile args, kwargs : ``func`` (keyword) arguments Returns ------- rbinned : np.ndarray, shape (N-1,) locations at which the binned profile is calculated. See note below. binned : np.ndarray, shape (N-1,) binned profile .. note :: The binned profile is returned at the mean ``r`` in each bin. If ``r`` is linearly-spaced then this will be the mid-point of each bin. If it is log-spaced then it is the logarithmic mid-point. .. py:class:: Profile(z=0, overdensity=500, *, z_s=None, los_loglimit=6, nsamples_los=200, resampling=20, logleft=-10, left_samples=100, **kwargs) Bases: :py:obj:`profiley.helpers.lensing.Lens` Profile object All profiles should inherit from ``Profile`` Defining your own profile is very simple. As an example, let's define a simple power-law profile with two free parameters, the normalization and the slope: .. math:: f(r | a, b) = a \, r^b The definition would look like this: .. code-block :: class PowerLaw(Profile): def __init__(self, norm, slope, **kwargs): self._set_shape(norm*slope) super().__init__(**kwargs) self.norm = norm self.slope = slope @array def profile(self, r): return self.norm * r**self.slope That's it! The ``__init__()`` method needs only two lines of code (in addition to attribute definitions). The last line is necessary to allow ``profiley`` to automatically handle arbitrary shapes, through the definition of a ``shape`` attribute. Note that ``_set_shape`` takes only one argument (besides ``self``) - the *product* of the class arguments. That is, if the arguments are arrays, their dimensions must be such that a product can be carried out without any manipulation. Profile projections ------------------- If the projection of this profile is analytical, any or all of the methods in the ``Profile`` base class can be implemented. If they do not have analytical expressions, these methods will also exist, but they will be calculated numerically, so they may be somewhat slower depending on the precision required. Cosmology --------- All ``Profile`` objects contain all cosmological information with which they have been initialized through the ``self.cosmo`` attribute, which can be any ``astropy.cosmology.FLRW`` object. .. py:property:: _one .. py:property:: _dimensions .. py:property:: equivalence_radius "Equivalence radius as defined in `Miller et al. (2016) <`_ .. py:property:: r_eq alias for ``self.equivalence_radius`` .. py:property:: shape .. py:method:: _assert_overdensity(overdensity) .. py:method:: _expand_dims(x) Add the appropriate number of dimensions to the end of x .. py:method:: _set_shape(args_product) .. py:method:: mdelta(overdensity: float, background: str, **kwargs) Calculate the mass out to a radius containing a specified overdensity See ``rdelta`` for help with parameters Returns ------- mdelta : np.ndarray masses within spheres of radius ``rdelta`` rdelta : np.ndarray radii enclosing the specified overdensity density_err : np.ndarray, optional fractional error in the density at the returned radii. Only returned if ``return_errors==True`` .. py:method:: rdelta(overdensity: float, background: str, *, return_errors: bool = False, rtol: float = 0.001, trial_range: float = 0.1, trial_size: int = 20, max_trials: int = 100, log_rmin: float = -10, integral_samples: int = 1000) Calculate the radius within which the density equals a specified overdensity Parameters ---------- overdensity : float The desired overdensity background : 'c' or 'm' Whether to use the critical or mean density as a reference rtol : float, optional The relative tolerance for convergence. The default is 0.001 trial_range : float, optional The fractional range to explore around the best radius in each iteration. Use a smaller number for smaller tolerances. The default is 0.1, corresponding to a 10% search in each iteration trial_size : int, optional The number of radii to try in each iteration. The default is 20 max_trials : int, optional The maximum number of trials to perform. If convergence is not reached a ``RuntimeWarning`` is raised. The default is 100 log_rmin : float, optional The minimum log10 radius to use for profile integration. See ``profile`` for details. The default is -10 integral_samples : int, optional number of samples to use in the integration. See ``profile`` for details. The default is 1000 return_errors : bool, optional whether to return fractional errors in the density at the returned radii. Default is False Returns ------- rdelta : np.ndarray The radius containing the specified overdenstiy density_err : np.ndarray, optional fractional error in the density at the returned radii. Only returned if ``return_errors==True`` Each returned array has a shape equal to ``self.shape``. .. py:method:: cumulative(r: numpy.ndarray, *, log_rmin: float = -10, integral_samples: int = 1000) -> numpy.ndarray Mean value of the profile within a radius r, .. math:: \bar\rho(R) = \frac3{R^3} \int_0^R r^2 \rho(r)\,dr Parameters ---------- r : np.ndarray positions at which to calculate the cumulative profile Optional arguments ------------------ log_rmin : float log10 lower limit to integrate the profile integral_samples : int number of samples to generate for Simpson-rule integration .. py:method:: escape_velocity(r: numpy.ndarray, *, accelerating: bool = True, **kwargs: dict) -> numpy.ndarray Escape velocity in :math:`\mathrm{km\,s^{-1}}`, given by .. math :: v_\mathrm{esc}(r) = \sqrt{2|\phi(r)|} where :math:`\phi(r)` is the gravitational potential. See ``self.potential`` for parameters .. py:method:: mass_cumulative(r: numpy.ndarray, *, log_rmin: float = -10, integral_samples: int = 1000) -> numpy.ndarray Spherical integral within a radius r, .. math:: M( numpy.ndarray Line of sight projected profile, calculated numerically Parameters ---------- R : np.ndarray positions at which to calculate the projected profile Optional arguments ------------------ log_rmin, log_rmax : float lower and upper limits for logspace resampling for integration integral_samples : int number of samples to generate for Simpson-rule integration of the projected profile .. note :: The default values for the integration parameters give numerical errors well below 0.1% over the range R=[1e-5,100] Mpc, when comparing the numerical and analytical implementations for an NFW profile (the former can be obtained by defining a GNFW profile with default kwargs) .. py:method:: potential(r: numpy.ndarray, *, accelerating: bool = True, log_rmin: float = -10, log_rmax: float = 5, integral_samples: int = 1000) -> numpy.ndarray Gravitational potential at radius ``r`` in units of :math:`\mathrm{Mpc^2\,s^{-2}}` Parameters ---------- R : float or array of float projected distance(s) accelerating : bool whether to calculate the potential in an accelerating universe, following Eq 7 of `Miller et al. (2016) `_. The total mass is taken to be :math:`M_\mathrm{200m}}` when defining the turnaround radius :math:`r_\mathrm{eq}`. Returns ------- potential : ndarray absolute value of the gravitational potential .. py:method:: projected_cumulative(R: numpy.ndarray, *, log_rmin: float = -10, left_samples: int = 100, resampling: int = 20, **kwargs) -> numpy.ndarray Cumulative projected profile within R, calculated numerically Parameters ---------- R : np.ndarray positions at which to calculate the projected profile Optional arguments ------------------ log_rmin : float lower limit for logspace resampling for integration. The same value will be passed to ``self.projected`` resampling : int number of samples into which each R-interval in the data will be re-sampled. For instance, if two adjacent data points are at ``Rbin=0.1,0.2`` then for the integration they will be replaced by :: newRbin = np.logspace( np.log10(0.1), np.log10(0.2), resampling, endpoint=False) (the endpoint will be added when sampling the following bin) left_samples : int number of samples to use between log_rmin and the first value of R, with a logarithmic sampling Additional arguments will be passed to ``self.projected`` .. note :: The default values for the integration parameters give numerical errors well below 0.1% over the range :math:`R=[10^{-5},100]\,\mathrm{Mpc}`, when comparing the numerical and analytical implementations for an NFW profile (the former can be obtained by defining a GNFW profile with default kwargs) .. py:method:: projected_excess(R: numpy.ndarray, log_rmin: float = -10, log_rmax: float = 6, integral_samples: int = 200, left_samples: int = 100, resampling: int = 20) -> numpy.ndarray Cumulative projected profile file excess at projected distance(s) R, defined as :: projected_excess(R) = projected_cumulative(R) - projected(R) This profile is most commonly used as the galaxy weak lensing *shear* observable, :math:`\gamma` where the projected excess is referred to as the *excess surface density* (ESD or :math:`\Delta\Sigma`), .. math:: \Delta\Sigma(R) = \gamma\Sigma_\mathrm{c} where :math:`\Sigma_\mathrm{c}` is the critical surface density Parameters ---------- R : float or array of float projected distance(s) Optional arguments are passed to either ``self.projected`` or ``self.projected_cumulative`` .. py:method:: offset(func: callable, R: numpy.ndarray, Roff: numpy.ndarray, theta_samples: int = 360, weights: numpy.ndarray = None, **kwargs) -> numpy.ndarray Calcuate any profile with a reference point different from its center .. note :: We recommend using the significantly faster ``profiley.numeric.offset`` instead of this method, and will merge that implementation into this function in the future. Parameters ---------- func : callable the funcion to calculate R : np.ndarray, shape (N,) radii at which to calculate the offset surface density Roff : np.ndarray, shape (M,) offsets with respect to the profile center Optional parameters ------------------- theta_samples : int number of samples for the angular integral from 0 to 2*pi weights : array of floats, shape (M,) weights to apply to each profile corresponding to every value of ``Roff``. See ``Returns`` below kwargs : dict arguments to pass to ``func`` Returns ------- offset : np.ndarray, offset profile. The shape of the array depends on whether the ``weights`` argument is specified: if *not* specified (default), then .. code-block :: shape: (M,N,*self.shape) if ``weights`` is provided, then the first axis will be weight-averaged over so that .. code-block :: shape: (N,*self.shape) .. py:method:: offset_profile(R, Roff, **kwargs) Alias for ``offset(profile, R, Roff, **kwargs)`` .. py:method:: offset_projected(R, Roff, **kwargs) Alias for ``offset(projected, R, Roff, **kwargs)`` .. py:method:: offset_projected_cumulative(R, Roff, **kwargs) Alias for ``offset(projected_cumulative, R, Roff, **kwargs)`` .. py:method:: offset_projected_excess(R, Roff, **kwargs) Alias for ``offset(projected_excess, R, Roff, **kwargs)``