pysersic ======== .. py:module:: pysersic Submodules ---------- .. toctree:: :maxdepth: 1 /autoapi/pysersic/exceptions/index /autoapi/pysersic/galfit/index /autoapi/pysersic/loss/index /autoapi/pysersic/multiband/index /autoapi/pysersic/priors/index /autoapi/pysersic/pysersic/index /autoapi/pysersic/rendering/index /autoapi/pysersic/results/index Classes ------- .. autoapisummary:: pysersic.PySersicMultiPrior pysersic.PySersicSourcePrior pysersic.SourceProperties pysersic.FitMulti pysersic.FitSingle pysersic.FourierRenderer pysersic.HybridRenderer pysersic.PixelRenderer pysersic.PySersicResults Functions --------- .. autoapisummary:: pysersic.autoprior pysersic.check_input_data pysersic.parse_mask pysersic.parse_multi_results Package Contents ---------------- .. py:class:: PySersicMultiPrior(catalog: Union[pandas.DataFrame, dict, numpy.recarray], sky_type: Optional[str] = 'none', sky_guess: Optional[float] = None, sky_guess_err: Optional[float] = None, suffix: Optional[str] = '') Bases: :py:obj:`BasePrior` Class used for priors for multi source fitting in PySersic .. py:attribute:: catalog :type: dict .. py:attribute:: N_sources :type: int .. py:attribute:: suffix :value: '' .. py:method:: __repr__() -> str .. py:class:: PySersicSourcePrior(profile_type: str, sky_type: Optional[str] = 'none', sky_guess: Optional[float] = None, sky_guess_err: Optional[float] = None, suffix: Optional[str] = '') Bases: :py:obj:`BasePrior` Class used for priors for single source fitting in PySersic .. py:attribute:: profile_type :type: str .. py:method:: __repr__() -> str .. py:method:: check_vars(verbose=False) -> bool Function to check if all variable for the specified profile type are set with no extras :param verbose: Wheter to print out missing and extra variables, by default False :type verbose: bool, optional :returns: True if all variable for given type are present with no extra, False otherwise :rtype: bool .. py:class:: SourceProperties(image: Union[numpy.array, jax.numpy.array], mask: Union[numpy.array, jax.numpy.array] = None) A Class used to estimate initial guesses for source properties. If no guesses are provided, then the class will estimate them using the `photutls` package and the `data_properties()` function. .. py:method:: measure_properties(**kwargs) -> SourceProperties Measure default properties of the source :returns: returns self :rtype: SourceProperties .. py:method:: set_sky_guess(sky_guess: Optional[float] = None, sky_guess_err: Optional[float] = None, n_pix_sample: int = 5, **kwargs) -> SourceProperties Measure or set guess for initial sky background level. If no estimate is provided, the median of the n_pix_sample number of pixels around each edge is used :param sky_guess: Initial guess for level of background, by default None :type sky_guess: Optional[float], optional :param sky_guess_err: Uncertainity on inital guess, by default None :type sky_guess_err: Optional[float], optional :param n_pix_sample: Number of pixels around each edge to use to estimate sky level if neccesary, by default 5 :type n_pix_sample: int, optional :returns: returns self :rtype: SourceProperties .. py:method:: set_flux_guess(flux_guess: Optional[float] = None, flux_guess_err: Optional[float] = None, **kwargs) -> SourceProperties Measure or set guess for initial flux. If no estimate is provided, the flux of the source in estimated as the total flux within the sgmentatated region for the source :param flux_guess: Initial guess for flux, by default None :type flux_guess: Optional[float], optional :param flux_guess_err: Uncertainty on initial guess, by default None :type flux_guess_err: Optional[float], optional :returns: returns self :rtype: SourceProperties .. py:method:: set_r_eff_guess(r_eff_guess: Optional[float] = None, r_eff_guess_err: Optional[float] = None, **kwargs) -> SourceProperties Measure or set guess for effective radius. If no estimate is provided, the r_eff of the source in estimated using photutils :param r_eff_guess: Initial guess for effective radius, by default None :type r_eff_guess: Optional[float], optional :param r_eff_guess_err: Uncertainty on initial guess, by default None :type r_eff_guess_err: Optional[float], optional :returns: returns self :rtype: SourceProperties .. py:method:: set_theta_guess(theta_guess: Optional[float] = None, **kwargs) -> SourceProperties Measure or set guess for initial position angle. If no estimate is provided, the position angle of the source in estimated using the data_properties() function from photutils :param theta_guess: Estimate of the position angle in radians, by default None :type theta_guess: Optional[float], optional :returns: returns self :rtype: SourceProperties .. py:method:: set_position_guess(position_guess: Optional[Iterable[float, float]] = None, **kwargs) -> SourceProperties Measure or set guess for initial position. If no estimate is provided, the position of the source in estimated using the data_properties() function from photutils :param position_guess: A 2 element list, tuple or array which contain the x,y pixel values of the inital guess for the centroid, by default None :type position_guess: Optional[Iterable[float,float]], optional :returns: returns self :rtype: SourceProperties .. py:method:: generate_prior(profile_type: str, sky_type: Optional[str] = 'none', suffix: Optional[str] = '') -> PySersicSourcePrior Function to generate default priors based on a given image and profile type :param profile_type: Type of profile :type profile_type: str :param sky_type: Type of sky model to use, must be one of: 'none', 'flat', 'tilted-plane' :type sky_type: str, default 'none' :param suffix: Add suffix onto all source related variables, generally only used to number sources in MultiPrior :type suffix: str, default '' :returns: Pysersic prior object to be used to initialize FitSingle :rtype: PySersicSourcePrior .. py:method:: visualize(figsize: Tuple[float, float] = (6.0, 6.0), cmap: str = 'gray', scale: float = 1.0) -> None Display a figure summarizing the current guess for the source properties :param figsize: figure, by default (6.,6.) :type figsize: Tuple[float,float], optional :param cmap: color map, by default 'gray' :type cmap: str, optional :param scale: number of +/- std's at which to clip image, by default 1 :type scale: float, optional .. py:function:: autoprior(image: numpy.array, profile_type: str, mask: numpy.array = None, sky_type: str = 'none') -> PySersicSourcePrior Simple wrapper function to generate a prior using the built-in defaults. This can be used as a starting place but may not work for all sources :param image: science image :type image: np.array :param profile_type: Type of profile to be fit :type profile_type: str :param mask: pixel by pixel mask, by default None :type mask: np.array, optional :param sky_type: Type of sky to fit, default 'none' :type sky_type: str :returns: Prior object that can be used in initializing FitSingle :rtype: PySersicSourcePrior .. py:class:: FitMulti(data: jax.typing.ArrayLike, rms: jax.typing.ArrayLike, psf: jax.typing.ArrayLike, prior: pysersic.priors.PySersicMultiPrior, mask: Optional[jax.typing.ArrayLike] = None, loss_func: Optional[Callable] = gaussian_loss, renderer: Optional[pysersic.rendering.BaseRenderer] = HybridRenderer, renderer_kwargs: Optional[dict] = {}) Bases: :py:obj:`BaseFitter` Class used to fit multiple sources within a single image .. py:attribute:: prior .. py:method:: build_model(return_model: bool = True) -> Callable Generate Numpyro model for the specified image, profile and priors :returns: **model** -- Function specifying the current model in Numpyro, can be passed to inference algorithms :rtype: Callable .. py:method:: find_MAP(return_model: Optional[bool] = True, rkey: Optional[jax.random.PRNGKey] = jax.random.PRNGKey(3), purge_extra: Optional[bool] = True) Find the "best-fit" parameters as the maximum a-posteriori and return a dictionary with values for the parameters. :param return_model: whether to return the model image, adds a small time and memory overhead, by default True :type return_model: Optional[bool], optional :param rkey: rng key, by default jax.random.PRNGKey(3) :type rkey: Optional[jax.random.PRNGKey], optional :returns: dictionary with fit parameters and their values. :rtype: dict .. py:class:: FitSingle(data: jax.typing.ArrayLike, rms: jax.typing.ArrayLike, psf: jax.typing.ArrayLike, prior: pysersic.priors.PySersicSourcePrior, mask: Optional[jax.typing.ArrayLike] = None, loss_func: Optional[Callable] = gaussian_loss, renderer: Optional[pysersic.rendering.BaseRenderer] = HybridRenderer, renderer_kwargs: Optional[dict] = {}) Bases: :py:obj:`BaseFitter` Class used to fit a single source .. py:attribute:: profile_type_number :value: 0 .. py:attribute:: prior .. py:method:: build_model(return_model: bool = True) -> Callable Generate Numpyro model for the specified image, profile and priors :returns: **model** -- Function specifying the current model in Numpyro, can be passed to inference algorithms :rtype: Callable .. py:function:: check_input_data(data: jax.typing.ArrayLike, rms: jax.typing.ArrayLike, psf: jax.typing.ArrayLike, mask: jax.typing.ArrayLike = None) Check input data for certain conditions and raise warnings or exceptions if needed :param data: input image (galaxy/cutout) :type data: ArrayLike :param rms: rms/error map of the data. :type rms: ArrayLike :param psf: pixelized PSF :type psf: ArrayLike :param mask: mask with True/1 indicating a pixel should be masked, by default None :type mask: ArrayLike, optional :raises RMSWarning: If the rms map is highly discrepant from the rms of the data :raises PSFNormalizationWarning: if the pst normalization is not ~1 :raises KernelError: if the provided PSF is larger than the input image (exception) :raises MaskWarning: If more than 70% of the image is masked. .. py:function:: parse_mask(mask: jax.typing.ArrayLike = None, data: jax.typing.ArrayLike = None) .. py:class:: FourierRenderer(im_shape: Iterable, pixel_PSF: jax.numpy.array, frac_start: Optional[float] = 0.01, frac_end: Optional[float] = 15.0, n_sigma: Optional[int] = 15, precision: Optional[int] = 10, use_interp_amps: Optional[bool] = True) Bases: :py:obj:`BaseRenderer` Class to render sources based on rendering them in Fourier space. Sersic profiles are modeled as a series of Gaussian following Shajib (2019) (https://arxiv.org/abs/1906.08263) and the implementation in lenstronomy (https://github.com/lenstronomy/lenstronomy/blob/main/lenstronomy/LensModel/Profiles/gauss_decomposition.py) .. py:attribute:: frac_start :type: float .. py:attribute:: frac_end :type: float .. py:attribute:: n_sigma :type: int .. py:attribute:: precision :type: int .. py:attribute:: use_interp_amps :type: bool .. py:attribute:: etas :type: jax.numpy.array .. py:attribute:: betas :type: jax.numpy.array .. py:attribute:: n_ax :type: jax.numpy.array .. py:attribute:: amps_n_ax :type: jax.numpy.array .. py:method:: get_amps_sigmas(flux, r_eff, n) .. py:method:: render_sersic_mog_fourier(xc, yc, flux, r_eff, n, ellip, theta) .. py:method:: render_sersic(params: dict) -> jax.numpy.array Render a Sersic profile :param xc: Central x position :type xc: float :param yc: Central y position :type yc: float :param flux: Total flux :type flux: float :param r_eff: Effective radius :type r_eff: float :param n: Sersic index :type n: float :param ellip: Ellipticity :type ellip: float :param theta: Position angle in radians :type theta: float :returns: Rendered Sersic model :rtype: jax.numpy.array .. py:method:: render_pointsource(params: dict) -> jax.numpy.array Render a Point source :param xc: Central x position :type xc: float :param yc: Central y position :type yc: float :param flux: Total flux :type flux: float :returns: rendered pointsource model :rtype: jax.numpy.array .. py:method:: combine_scene(F_im, int_im, obs_im) Combine scene for FourierRenderer where everything is rendered in Fourier space :param F_im: Sum of sources rendered in Fourier space :type F_im: Fourier image :param int_im: Sum of sources rendered in Intrinsic space :type int_im: _type_ :param obs_im: Sum of sources rendered in observed space :type obs_im: _type_ :returns: Combination of all sources to be compared to observations :rtype: Model image .. py:class:: HybridRenderer(im_shape: Iterable, pixel_PSF: jax.numpy.array, frac_start: Optional[float] = 0.01, frac_end: Optional[float] = 15.0, n_sigma: Optional[int] = 15, num_pixel_render: Optional[int] = 3, precision: Optional[int] = 10, use_interp_amps: Optional[bool] = True) Bases: :py:obj:`FourierRenderer` Class to render sources based on the hybrid rendering scheme introduced in Lang (2020). This avoids some of the artifacts introduced by rendering sources purely in Fourier space. Sersic profiles are modeled as a series of Gaussian following Shajib (2019) (https://arxiv.org/abs/1906.08263) and the implementation in lenstronomy (https://github.com/lenstronomy/lenstronomy/blob/main/lenstronomy/LensModel/Profiles/gauss_decomposition.py). Our scheme is implemented slightly differently than Lang (2020), specifically in how it chooses which gaussian components to render in Fourier vs. Real space. Lang (2020) employs a cutoff based on distance to the edge of the image. However given some of jax's limitation with dynamic shapes (see more here -> https://jax.readthedocs.io/en/latest/notebooks/Common_Gotchas_in_JAX.html#dynamic-shapes), we have not implemented that specific criterion. Instead we use a simpler version where the user must decide how many components to render in real space, starting from the largest ones. While this is not ideal in all circumstances it still overcomes many of the issues of rendering purely in fourier space discussed in Lang (2020). .. py:attribute:: num_pixel_render :type: int .. py:attribute:: w_real :type: jax.numpy.array .. py:attribute:: w_fourier :type: jax.numpy.array .. py:attribute:: sig_psf_approx :type: float .. py:method:: render_sersic_hybrid(xc, yc, flux, r_eff, n, ellip, theta) .. py:method:: render_sersic(params: dict) -> jax.numpy.array Render a Sersic profile :param xc: Central x position :type xc: float :param yc: Central y position :type yc: float :param flux: Total flux :type flux: float :param r_eff: Effective radius :type r_eff: float :param n: Sersic index :type n: float :param ellip: Ellipticity :type ellip: float :param theta: Position angle in radians :type theta: float :returns: Rendered Sersic model :rtype: jax.numpy.array .. py:method:: render_pointsource(params: dict) -> jax.numpy.array Render a Point source :param xc: Central x position :type xc: float :param yc: Central y position :type yc: float :param flux: Total flux :type flux: float :returns: rendered pointsource model :rtype: jax.numpy.array .. py:method:: combine_scene(F_im, int_im, obs_im) Combine scene for FourierRenderer where nothing is rendered in Intrinsic space :param F_im: Sum of sources rendered in Fourier space :type F_im: Fourier image :param int_im: Sum of sources rendered in Intrinsic space :type int_im: _type_ :param obs_im: Sum of sources rendered in observed space :type obs_im: _type_ :returns: Combination of all sources to be compared to observations :rtype: Model image .. py:class:: PixelRenderer(im_shape: Iterable, pixel_PSF: jax.numpy.array, os_pixel_size: Optional[int] = 6, num_os: Optional[int] = 12) Bases: :py:obj:`BaseRenderer` Render class based on rendering in pixel space and then convolving with the PSF .. py:attribute:: os_pixel_size :type: int .. py:attribute:: num_os :type: int .. py:attribute:: w_os :type: jax.numpy.array .. py:attribute:: x_os_lo :type: int .. py:attribute:: x_os_hi :type: int .. py:attribute:: y_os_lo :type: int .. py:attribute:: y_os_hi :type: int .. py:attribute:: X_os :type: jax.numpy.array .. py:attribute:: Y_os :type: jax.numpy.array .. py:method:: render_int_sersic(xc, yc, flux, r_eff, n, ellip, theta) .. py:method:: render_sersic(params: dict) -> jax.numpy.array Render a sersic profile :param xc: Central x position :type xc: float :param yc: Central y position :type yc: float :param flux: Total flux :type flux: float :param r_eff: Effective radius :type r_eff: float :param n: Sersic index :type n: float :param ellip: Ellipticity :type ellip: float :param theta: Position angle in radians :type theta: float :returns: Rendered Sersic model :rtype: jax.numpy.array .. py:method:: render_pointsource(params: dict) -> jax.numpy.array Render a Point source by interpolating given PSF into image. Currently jax only supports linear intepolation. :param xc: Central x position :type xc: float :param yc: Central y position :type yc: float :param flux: Total flux :type flux: float :returns: rendered pointsource model :rtype: jax.numpy.array .. py:method:: combine_scene(F_im, int_im, obs_im) Combine scene for PixelRenderer when nothing is rendered in Fourier space :param F_im: Sum of sources rendered in Fourier space :type F_im: Fourier image :param int_im: Sum of sources rendered in Intrinsic space :type int_im: _type_ :param obs_im: Sum of sources rendered in observed space :type obs_im: _type_ :returns: Combination of all sources to be compared to observations :rtype: Model image .. py:class:: PySersicResults(data: ArrayLike, rms: ArrayLike, psf: ArrayLike, loss_func: Callable, renderer: pysersic.rendering.BaseRenderer, mask: Optional[ArrayLike] = None) .. py:attribute:: data .. py:attribute:: rms .. py:attribute:: psf .. py:attribute:: mask :value: None .. py:attribute:: loss_func .. py:attribute:: renderer .. py:method:: __repr__() -> str .. py:method:: add_method_used(method) .. py:method:: add_prior(prior) add the prior object to the result object once created :param prior: created prior object :type prior: pysersic.priors.BasePrior .. py:method:: injest_data(sampler: Optional[numpyro.infer.mcmc.MCMC] = None, svi_res_dict: Optional[dict] = None, purge_extra: Optional[bool] = True, num_sample: Optional[int] = 1000, rkey: Optional[jax.random.PRNGKey] = random.PRNGKey(5)) -> pandas.DataFrame Method to injest data from optimized SVI model or results of sampling. When sampling data is input, sets the class attribute `sampling_results`, while for an SVI run, saves `svi_results.` A PysersicResults object can have at most data for one SVI and one sampling run. Both class atttributes are instances of arviz.InferenceData objects. :param sampler: numpyro sampler containing results :type sampler: Optional[numpyro.infer.mcmc.MCMC], optional :param svi_res_dict: Dictionary containing 'guide', 'model' and 'svi_result' specifying a trained SVI model :type svi_res_dict: Optional[dict], optional :param purge_extra: Whether to purge variables containing 'auto', 'base' or 'unwrapped' often used in reparamaterization, by default True :type purge_extra: Optional[bool], optional :param num_sample: Number of samples to draw from trained SVI posterior, no effect if sampling was used. :type num_sample: Optional[int] :param rkey: PRNG key to use, by default jax.random.PRNGKey(5) :type rkey: Optional[jax.random.PRNGKey], optional :returns: ArviZ Summary of results :rtype: pandas.DataFrame :raises AssertionError: Must supply one of sampler or svi_dict .. py:method:: _parse_injested_data(data: arviz.InferenceData, purge_extra: bool = True, save_model: bool = True) -> arviz.InferenceData Helper function to postprocess the poterior object (internal use). :param data: _description_ :type data: arviz.InferenceData :param purge_extra: whether to purge extra params not part of the fitting, by default True :type purge_extra: bool, optional :param save_model: Whether to set self.models with model images from posterior :type save_model: bool :returns: the cleaned up object :rtype: arviz.InferenceData .. py:method:: summary() -> pandas.DataFrame Convenience function for returning the summary dataframe using the arviz summary. :returns: data frame containing the arviz summary of the fit. :rtype: pandas.DataFrame .. py:method:: get_median_model() .. py:method:: corner(quantiles=[0.16, 0.5, 0.84], **kwargs) Return a corner plot of the parameter estimation :param quantiles: which quantiles to mark on the corner plot, can pass None, by default [0.16,0.5,0.84] :type quantiles: ListLike, optional :param \*\*kwargs: any additional arguments to pass to corner (for the single plot case). :returns: fig object containing the corner plots :rtype: matplotlib.figure .. py:method:: retrieve_param_quantiles(quantiles: ListLike = [0.16, 0.5, 0.84], return_dataframe: bool = False) -> Union[pandas.DataFrame, dict] retrieve quantiles on the parameter estimation :param quantiles: array of quantiles to pull, must be between 0 and 1, by default [0.16,0.5,0.84] :type quantiles: ListLike, optional :param return_dataframe: whether to return dataframe instead of simple dict, by default False :type return_dataframe: bool, optional :returns: dict or dataframe with index/keys as parameters and columns/values as the chosen quantiles. :rtype: Union[pd.DataFrame,dict] .. py:method:: retrieve_med_std(return_dataframe: bool = False) -> Union[pandas.DataFrame, dict] .. py:method:: latex_table(quantiles: ListLike = [0.16, 0.5, 0.84]) Generate a simple AASTex deluxetable with the fit parameters. Prints the result. :param quantiles: quantiles to use must be len 3 as we do upper-median and median-lower to get +/- values, by default [0.16,0.5,0.84] :type quantiles: ListLike, optional :raises AssertionError: if the quantile list does not have three values .. py:method:: get_chains() -> xarray.Dataset Wrapper for az.extract, producing the chains/draws for the run :returns: chain object :rtype: xarray.Dataset .. py:method:: compute_statistic(parameter: str, func: Callable) -> ArrayLike Compute an arbitrary array statistic on the chain for a given parameter. For example, the std of all ellipticity draws. or the mean of the sersic draws :param parameter: a legal parameter from the fit. Use e.g. `results.svi_summary()` to see them. :type parameter: str :param func: any function which reads in array-like data and computes something :type func: Callable :returns: the computed statistic(s) :rtype: ArrayLike .. py:method:: save_result(fname: str) Save summary of the fit, copies of the data, the chains, and some other info about priors and renderers into an asdf file for later retrieval. :param fname: filename to save to. :type fname: str .. py:method:: sample_posterior(num_sample: int, purge_extra: Optional[bool] = True, rkey: Optional[jax.random.PRNGKey] = random.PRNGKey(7)) -> arviz.InferenceData Generate extra samples from an trained SVI posterior :param num_sample: number of samples to draw :type num_sample: int :param purge_extra: Whether to purge variables containing 'auto', 'base' or 'unwrapped' often used in reparamaterization, by default True :type purge_extra: Optional[bool], optional :param rkey: PRNG key to use, by default jax.random.PRNGKey(7) :type rkey: Optional[jax.random.PRNGKey], optional :returns: arviz InferenceData object containing posterior :rtype: az.InferenceData .. py:function:: parse_multi_results(results: PySersicResults, source_num: int) -> PySersicResults Function written to parse results from a FitMulti instance to isolate a single source. A new PySersicResults class is created with only the posteriors of the specified source. The original chains saved under `.idata_all` :param results: Results class from a FitMulti instance :type results: PySersicResults :param source_num: Source number to isolate or if equal to -1 will reset to the joint posterior of all sources :type source_num: int :returns: Results class with all the meta-data the same but the specified source isolated. The original posterior is saved under `.idata_all` :rtype: PySersicResults