def deflections_of_galaxies_from_grid(grid, galaxies):
    """Compute the deflections of a list of galaxies from an input grid, by summing the individual deflections \
    of each galaxy's mass profile.

    If the input grid is a *grids.SubGrid*, the potential is calculated on the sub-grid and binned-up to the \
    original regular grid by taking the mean value of every set of sub-pixels.

    If no galaxies are entered into the function, an array of all zeros is returned.

    Parameters
    -----------
    grid : RegularGrid
        The grid (regular or sub) of (y,x) arc-second coordinates at the centre of every unmasked pixel which the \
        deflections is calculated on.
    galaxies : [galaxy.Galaxy]
        The galaxies whose mass profiles are used to compute the surface densities.
    """
    if len(galaxies) > 0:
        deflections = sum(map(lambda galaxy: galaxy.deflections_from_grid(grid), galaxies))
    else:
        deflections = np.full((grid.shape[0], 2), 0.0)

    if isinstance(grid, grids.SubGrid):
        return np.asarray([grid.regular_data_1d_from_sub_data_1d(deflections[:, 0]),
                           grid.regular_data_1d_from_sub_data_1d(deflections[:, 1])]).T

    return deflections

def deflections_of_galaxies_from_grid ( grid , galaxies ) : if len ( galaxies ) > 0 : deflections = sum ( map ( lambda galaxy : galaxy . deflections_from_grid ( grid ) , galaxies ) ) else : deflections = np . full ( ( grid . shape [ 0 ] , 2 ) , 0.0 ) if isinstance ( grid , grids . SubGrid ) : return np . asarray ( [ grid . regular_data_1d_from_sub_data_1d ( deflections [ : , 0 ] ) , grid . regular_data_1d_from_sub_data_1d ( deflections [ : , 1 ] ) ] ) . T return deflections

Compute the deflections of a list of galaxies from an input grid by summing the individual deflections \ of each galaxy s mass profile .


def deflections_of_galaxies_from_sub_grid(sub_grid, galaxies):
    """Compute the deflections of a list of galaxies from an input sub-grid, by summing the individual deflections \
    of each galaxy's mass profile.

    The deflections are calculated on the sub-grid and binned-up to the original regular grid by taking the mean value \
    of every set of sub-pixels.

    If no galaxies are entered into the function, an array of all zeros is returned.

    Parameters
    -----------
    sub_grid : RegularGrid
        The grid (regular or sub) of (y,x) arc-second coordinates at the centre of every unmasked pixel which the \
        deflections is calculated on.
    galaxies : [galaxy.Galaxy]
        The galaxies whose mass profiles are used to compute the surface densities.
    """
    if galaxies:
        return sum(map(lambda galaxy: galaxy.deflections_from_grid(sub_grid), galaxies))
    else:
        return np.full((sub_grid.shape[0], 2), 0.0)

def deflections_of_galaxies_from_sub_grid ( sub_grid , galaxies ) : if galaxies : return sum ( map ( lambda galaxy : galaxy . deflections_from_grid ( sub_grid ) , galaxies ) ) else : return np . full ( ( sub_grid . shape [ 0 ] , 2 ) , 0.0 )

Compute the deflections of a list of galaxies from an input sub - grid by summing the individual deflections \ of each galaxy s mass profile .


def blurred_image_of_planes_from_1d_images_and_convolver(total_planes, image_plane_image_1d_of_planes,
                                                         image_plane_blurring_image_1d_of_planes, convolver,
                                                         map_to_scaled_array):
    """For a tracer, extract the image-plane image of every plane and blur it with the PSF.

    If none of the galaxies in a plane have a light profie or pixelization (and thus don't have an image) a *None* \
    is used.

    Parameters
    ----------
    total_planes : int
        The total number of planes that blurred images are computed for.
    image_plane_image_1d_of_planes : [ndarray]
        For every plane, the 1D image-plane image.
    image_plane_blurring_image_1d_of_planes : [ndarray]
        For every plane, the 1D image-plane blurring image.
    convolver : hyper.ccd.convolution.ConvolverImage
        Class which performs the PSF convolution of a masked image in 1D.
    map_to_scaled_array : func
        A function which maps a masked image from 1D to 2D.
    """

    blurred_image_of_planes = []

    for plane_index in range(total_planes):

        # If all entries are zero, there was no light profile / pixeization
        if np.count_nonzero(image_plane_image_1d_of_planes[plane_index]) > 0:

            blurred_image_1d_of_plane = blurred_image_1d_from_1d_unblurred_and_blurring_images(
                unblurred_image_1d=image_plane_image_1d_of_planes[plane_index],
                blurring_image_1d=image_plane_blurring_image_1d_of_planes[plane_index],
                convolver=convolver)

            blurred_image_of_plane = map_to_scaled_array(array_1d=blurred_image_1d_of_plane)

            blurred_image_of_planes.append(blurred_image_of_plane)

        else:

            blurred_image_of_planes.append(None)

    return blurred_image_of_planes

For a tracer extract the image - plane image of every plane and blur it with the PSF .


def unmasked_blurred_image_of_planes_from_padded_grid_stack_and_psf(planes, padded_grid_stack, psf):
    """For lens data, compute the unmasked blurred image of every unmasked unblurred image of each plane. To do this, \
    this function iterates over all planes to extract their unmasked unblurred images.

    If a galaxy in a plane has a pixelization, the unmasked image is returned as None, as as the inversion's model \
    image cannot be mapped to an unmasked version.

    This relies on using the lens data's padded-grid, which is a grid of (y,x) coordinates which extends over the \
    entire image as opposed to just the masked region.

    This returns a list, where each list index corresponds to [plane_index].

    Parameters
    ----------
    planes : [plane.Plane]
        The list of planes the unmasked blurred images are computed using.
    padded_grid_stack : grids.GridStack
        A padded-grid_stack, whose padded grid is used for PSF convolution.
    psf : ccd.PSF
        The PSF of the image used for convolution.
    """
    unmasked_blurred_image_of_planes = []

    for plane in planes:

        if plane.has_pixelization:
            unmasked_blurred_image_of_plane = None
        else:
            unmasked_blurred_image_of_plane = \
                padded_grid_stack.unmasked_blurred_image_from_psf_and_unmasked_image(

                    psf=psf, unmasked_image_1d=plane.image_plane_image_1d)

        unmasked_blurred_image_of_planes.append(unmasked_blurred_image_of_plane)

    return unmasked_blurred_image_of_planes

For lens data compute the unmasked blurred image of every unmasked unblurred image of each plane . To do this \ this function iterates over all planes to extract their unmasked unblurred images .


def unmasked_blurred_image_of_planes_and_galaxies_from_padded_grid_stack_and_psf(planes, padded_grid_stack, psf):
    """For lens data, compute the unmasked blurred image of every unmasked unblurred image of every galaxy in each \
    plane. To do this, this function iterates over all planes and then galaxies to extract their unmasked unblurred \
    images.

    If a galaxy in a plane has a pixelization, the unmasked image of that galaxy in the plane is returned as None \
    as as the inversion's model image cannot be mapped to an unmasked version.

    This relies on using the lens data's padded-grid, which is a grid of (y,x) coordinates which extends over the \
    entire image as opposed to just the masked region.

    This returns a list of lists, where each list index corresponds to [plane_index][galaxy_index].

    Parameters
    ----------
    planes : [plane.Plane]
        The list of planes the unmasked blurred images are computed using.
    padded_grid_stack : grids.GridStack
        A padded-grid_stack, whose padded grid is used for PSF convolution.
    psf : ccd.PSF
        The PSF of the image used for convolution.
    """
    return [plane.unmasked_blurred_image_of_galaxies_from_psf(padded_grid_stack, psf) for plane in planes]

For lens data compute the unmasked blurred image of every unmasked unblurred image of every galaxy in each \ plane . To do this this function iterates over all planes and then galaxies to extract their unmasked unblurred \ images .


def contribution_maps_1d_from_hyper_images_and_galaxies(hyper_model_image_1d, hyper_galaxy_images_1d, hyper_galaxies,
                                                        hyper_minimum_values):
    """For a fitting hyper_galaxy_image, hyper_galaxy model image, list of hyper galaxies images and model hyper galaxies, compute
    their contribution maps, which are used to compute a scaled-noise_map map. All quantities are masked 1D arrays.

    The reason this is separate from the *contributions_from_fitting_hyper_images_and_hyper_galaxies* function is that
    each hyper_galaxy image has a list of hyper galaxies images and associated hyper galaxies (one for each galaxy). Thus,
    this function breaks down the calculation of each 1D masked contribution map and returns them in the same datas
    structure (2 lists with indexes [image_index][contribution_map_index].

    Parameters
    ----------
    hyper_model_image_1d : ndarray
        The best-fit model image to the datas (e.g. from a previous analysis phase).
    hyper_galaxy_images_1d : [ndarray]
        The best-fit model image of each hyper galaxy to the datas (e.g. from a previous analysis phase).
    hyper_galaxies : [galaxy.Galaxy]
        The hyper galaxies which represent the model components used to scale the noise_map, which correspond to
        individual galaxies in the image.
    hyper_minimum_values : [float]
        The minimum value of each hyper_galaxy-image contribution map, which ensure zero's don't impact the scaled noise-map.
    """
    # noinspection PyArgumentList
    return list(map(lambda hyper_galaxy, hyper_galaxy_image_1d, hyper_minimum_value:
                    hyper_galaxy.contributions_from_model_image_and_galaxy_image(model_image=hyper_model_image_1d,
                                                                                galaxy_image=hyper_galaxy_image_1d,
                                                                                minimum_value=hyper_minimum_value),
                    hyper_galaxies, hyper_galaxy_images_1d, hyper_minimum_values))

For a fitting hyper_galaxy_image hyper_galaxy model image list of hyper galaxies images and model hyper galaxies compute their contribution maps which are used to compute a scaled - noise_map map . All quantities are masked 1D arrays .


def scaled_noise_map_from_hyper_galaxies_and_contribution_maps(contribution_maps, hyper_galaxies, noise_map):
    """For a contribution map and noise-map, use the model hyper galaxies to compute a scaled noise-map.

    Parameters
    -----------
    contribution_maps : ndarray
        The image's list of 1D masked contribution maps (e.g. one for each hyper galaxy)
    hyper_galaxies : [galaxy.Galaxy]
        The hyper galaxies which represent the model components used to scale the noise_map, which correspond to
        individual galaxies in the image.
    noise_map : ccd.NoiseMap or ndarray
        An array describing the RMS standard deviation error in each pixel, preferably in units of electrons per
        second.
    """
    scaled_noise_maps = list(map(lambda hyper_galaxy, contribution_map:
                                 hyper_galaxy.hyper_noise_from_contributions(noise_map=noise_map,
                                                                             contributions=contribution_map),
                                 hyper_galaxies, contribution_maps))
    return noise_map + sum(scaled_noise_maps)

For a contribution map and noise - map use the model hyper galaxies to compute a scaled noise - map .


def for_data_and_tracer(cls, lens_data, tracer, padded_tracer=None):
        """Fit lens data with a model tracer, automatically determining the type of fit based on the \
        properties of the galaxies in the tracer.

        Parameters
        -----------
        lens_data : lens_data.LensData or lens_data.LensDataHyper
            The lens-images that is fitted.
        tracer : ray_tracing.TracerNonStack
            The tracer, which describes the ray-tracing and strong lens configuration.
        padded_tracer : ray_tracing.Tracer or None
            A tracer with an identical strong lens configuration to the tracer above, but using the lens data's \
            padded grid_stack such that unmasked model-images can be computed.
        """

        if tracer.has_light_profile and not tracer.has_pixelization:
            return LensProfileFit(lens_data=lens_data, tracer=tracer, padded_tracer=padded_tracer)
        elif not tracer.has_light_profile and tracer.has_pixelization:
            return LensInversionFit(lens_data=lens_data, tracer=tracer, padded_tracer=None)
        elif tracer.has_light_profile and tracer.has_pixelization:
            return LensProfileInversionFit(lens_data=lens_data, tracer=tracer, padded_tracer=None)
        else:
            raise exc.FittingException('The fit routine did not call a Fit class - check the '
                                       'properties of the tracer')

Fit lens data with a model tracer automatically determining the type of fit based on the \ properties of the galaxies in the tracer .


def transform_grid(func):
    """Wrap the function in a function that checks whether the coordinates have been transformed. If they have not \
    been transformed then they are transformed.

    Parameters
    ----------
    func : (profiles, *args, **kwargs) -> Object
        A function that requires transformed coordinates

    Returns
    -------
        A function that can except cartesian or transformed coordinates
    """

    @wraps(func)
    def wrapper(profile, grid, *args, **kwargs):
        """

        Parameters
        ----------
        profile : GeometryProfile
            The profiles that owns the function
        grid : ndarray
            PlaneCoordinates in either cartesian or profiles coordinate system
        args
        kwargs

        Returns
        -------
            A value or coordinate in the same coordinate system as those passed in.
        """
        if not isinstance(grid, TransformedGrid):
            return func(profile, profile.transform_grid_to_reference_frame(grid), *args, **kwargs)
        else:
            return func(profile, grid, *args, **kwargs)

    return wrapper

Wrap the function in a function that checks whether the coordinates have been transformed . If they have not \ been transformed then they are transformed .


def cache(func):
    """
    Caches results of a call to a grid function. If a grid that evaluates to the same byte value is passed into the same
    function of the same instance as previously then the cached result is returned.

    Parameters
    ----------
    func
        Some instance method that takes a grid as its argument

    Returns
    -------
    result
        Some result, either newly calculated or recovered from the cache
    """

    def wrapper(instance: GeometryProfile, grid: np.ndarray, *args, **kwargs):
        if not hasattr(instance, "cache"):
            instance.cache = {}
        key = (func.__name__, grid.tobytes())
        if key not in instance.cache:
            instance.cache[key] = func(instance, grid)
        return instance.cache[key]

    return wrapper

def cache ( func ) : def wrapper ( instance : GeometryProfile , grid : np . ndarray , * args , * * kwargs ) : if not hasattr ( instance , "cache" ) : instance . cache = { } key = ( func . __name__ , grid . tobytes ( ) ) if key not in instance . cache : instance . cache [ key ] = func ( instance , grid ) return instance . cache [ key ] return wrapper

Caches results of a call to a grid function . If a grid that evaluates to the same byte value is passed into the same function of the same instance as previously then the cached result is returned .


def cos_and_sin_from_x_axis(self):
        """ Determine the sin and cosine of the angle between the profile's ellipse and the positive x-axis, \
        counter-clockwise. """
        phi_radians = np.radians(self.phi)
        return np.cos(phi_radians), np.sin(phi_radians)

def cos_and_sin_from_x_axis ( self ) : phi_radians = np . radians ( self . phi ) return np . cos ( phi_radians ) , np . sin ( phi_radians )

Determine the sin and cosine of the angle between the profile s ellipse and the positive x - axis \ counter - clockwise .


def grid_angle_to_profile(self, grid_thetas):
        """The angle between each angle theta on the grid and the profile, in radians.

        Parameters
        -----------
        grid_thetas : ndarray
            The angle theta counter-clockwise from the positive x-axis to each coordinate in radians.
        """
        theta_coordinate_to_profile = np.add(grid_thetas, - self.phi_radians)
        return np.cos(theta_coordinate_to_profile), np.sin(theta_coordinate_to_profile)

def grid_angle_to_profile ( self , grid_thetas ) : theta_coordinate_to_profile = np . add ( grid_thetas , - self . phi_radians ) return np . cos ( theta_coordinate_to_profile ) , np . sin ( theta_coordinate_to_profile )

The angle between each angle theta on the grid and the profile in radians .


def mapping_matrix_from_sub_to_pix(sub_to_pix, pixels, regular_pixels, sub_to_regular, sub_grid_fraction):
    """Computes the mapping matrix, by iterating over the known mappings between the sub-grid and pixelization.

    Parameters
    -----------
    sub_to_pix : ndarray
        The mappings between the observed regular's sub-pixels and pixelization's pixels.
    pixels : int
        The number of pixels in the pixelization.
    regular_pixels : int
        The number of datas pixels in the observed datas and thus on the regular grid.
    sub_to_regular : ndarray
        The mappings between the observed regular's sub-pixels and observed regular's pixels.
    sub_grid_fraction : float
        The fractional area each sub-pixel takes up in an regular-pixel.
    """

    mapping_matrix = np.zeros((regular_pixels, pixels))

    for sub_index in range(sub_to_regular.shape[0]):
        mapping_matrix[sub_to_regular[sub_index], sub_to_pix[sub_index]] += sub_grid_fraction

    return mapping_matrix

def mapping_matrix_from_sub_to_pix ( sub_to_pix , pixels , regular_pixels , sub_to_regular , sub_grid_fraction ) : mapping_matrix = np . zeros ( ( regular_pixels , pixels ) ) for sub_index in range ( sub_to_regular . shape [ 0 ] ) : mapping_matrix [ sub_to_regular [ sub_index ] , sub_to_pix [ sub_index ] ] += sub_grid_fraction return mapping_matrix

Computes the mapping matrix by iterating over the known mappings between the sub - grid and pixelization .


def voronoi_regular_to_pix_from_grids_and_geometry(regular_grid, regular_to_nearest_pix, pixel_centres,
                                                   pixel_neighbors, pixel_neighbors_size):
    """ Compute the mappings between a set of regular-grid pixels and pixelization pixels, using information on \
    how regular pixels map to their closest pixelization pixel on the image-plane pix-grid and the pixelization's \
    pixel centres.

    To determine the complete set of regular-pixel to pixelization pixel mappings, we must pair every regular-pixel to \
    its nearest pixel. Using a full nearest neighbor search to do this is slow, thus the pixel neighbors (derived via \
    the Voronoi grid) are used to localize each nearest neighbor search via a graph search.

    Parameters
    ----------
    regular_grid : RegularGrid
        The grid of (y,x) arc-second coordinates at the centre of every unmasked pixel, which has been traced to \
        to an irregular grid via lens.
    regular_to_nearest_pix : ndarray
        A 1D array that maps every regular-grid pixel to its nearest pix-grid pixel (as determined on the unlensed \
        2D array).
    pixel_centres : ndarray
        The (y,x) centre of every Voronoi pixel in arc-seconds.
    pixel_neighbors : ndarray
        An array of length (voronoi_pixels) which provides the index of all neighbors of every pixel in \
        the Voronoi grid (entries of -1 correspond to no neighbor).
    pixel_neighbors_size : ndarray
        An array of length (voronoi_pixels) which gives the number of neighbors of every pixel in the \
        Voronoi grid.
     """

    regular_to_pix = np.zeros((regular_grid.shape[0]))

    for regular_index in range(regular_grid.shape[0]):

        nearest_pix_pixel_index = regular_to_nearest_pix[regular_index]

        while True:

            nearest_pix_pixel_center = pixel_centres[nearest_pix_pixel_index]

            sub_to_nearest_pix_distance = (regular_grid[regular_index, 0] - nearest_pix_pixel_center[0]) ** 2 + \
                                          (regular_grid[regular_index, 1] - nearest_pix_pixel_center[1]) ** 2

            closest_separation_from_pix_neighbor = 1.0e8

            for neighbor_index in range(pixel_neighbors_size[nearest_pix_pixel_index]):

                neighbor = pixel_neighbors[nearest_pix_pixel_index, neighbor_index]

                separation_from_neighbor = (regular_grid[regular_index, 0] - pixel_centres[neighbor, 0]) ** 2 + \
                                           (regular_grid[regular_index, 1] - pixel_centres[neighbor, 1]) ** 2

                if separation_from_neighbor < closest_separation_from_pix_neighbor:

                    closest_separation_from_pix_neighbor = separation_from_neighbor
                    closest_neighbor_index = neighbor_index

            neighboring_pix_pixel_index = pixel_neighbors[nearest_pix_pixel_index, closest_neighbor_index]
            sub_to_neighboring_pix_distance = closest_separation_from_pix_neighbor

            if sub_to_nearest_pix_distance <= sub_to_neighboring_pix_distance:
                regular_to_pix[regular_index] = nearest_pix_pixel_index
                break
            else:
                nearest_pix_pixel_index = neighboring_pix_pixel_index

    return regular_to_pix

Compute the mappings between a set of regular - grid pixels and pixelization pixels using information on \ how regular pixels map to their closest pixelization pixel on the image - plane pix - grid and the pixelization s \ pixel centres .


def voronoi_sub_to_pix_from_grids_and_geometry(sub_grid, regular_to_nearest_pix, sub_to_regular, pixel_centres,
                                               pixel_neighbors, pixel_neighbors_size):
    """ Compute the mappings between a set of sub-grid pixels and pixelization pixels, using information on \
    how the regular pixels hosting each sub-pixel map to their closest pixelization pixel on the image-plane pix-grid \
    and the pixelization's pixel centres.

    To determine the complete set of sub-pixel to pixelization pixel mappings, we must pair every sub-pixel to \
    its nearest pixel. Using a full nearest neighbor search to do this is slow, thus the pixel neighbors (derived via \
    the Voronoi grid) are used to localize each nearest neighbor search by using a graph search.

    Parameters
    ----------
    regular_grid : RegularGrid
        The grid of (y,x) arc-second coordinates at the centre of every unmasked pixel, which has been traced to \
        to an irregular grid via lens.
    regular_to_nearest_pix : ndarray
        A 1D array that maps every regular-grid pixel to its nearest pix-grid pixel (as determined on the unlensed \
        2D array).
    pixel_centres : (float, float)
        The (y,x) centre of every Voronoi pixel in arc-seconds.
    pixel_neighbors : ndarray
        An array of length (voronoi_pixels) which provides the index of all neighbors of every pixel in \
        the Voronoi grid (entries of -1 correspond to no neighbor).
    pixel_neighbors_size : ndarray
        An array of length (voronoi_pixels) which gives the number of neighbors of every pixel in the \
        Voronoi grid.
     """

    sub_to_pix = np.zeros((sub_grid.shape[0]))

    for sub_index in range(sub_grid.shape[0]):

        nearest_pix_pixel_index = regular_to_nearest_pix[sub_to_regular[sub_index]]

        while True:

            nearest_pix_pixel_center = pixel_centres[nearest_pix_pixel_index]

            sub_to_nearest_pix_distance = (sub_grid[sub_index, 0] - nearest_pix_pixel_center[0]) ** 2 + \
                                          (sub_grid[sub_index, 1] - nearest_pix_pixel_center[1]) ** 2

            closest_separation_from_pix_to_neighbor = 1.0e8

            for neighbor_index in range(pixel_neighbors_size[nearest_pix_pixel_index]):

                neighbor = pixel_neighbors[nearest_pix_pixel_index, neighbor_index]

                separation_from_neighbor = (sub_grid[sub_index, 0] - pixel_centres[neighbor, 0]) ** 2 + \
                                           (sub_grid[sub_index, 1] - pixel_centres[neighbor, 1]) ** 2

                if separation_from_neighbor < closest_separation_from_pix_to_neighbor:
                    closest_separation_from_pix_to_neighbor = separation_from_neighbor
                    closest_neighbor_index = neighbor_index

            neighboring_pix_pixel_index = pixel_neighbors[nearest_pix_pixel_index, closest_neighbor_index]
            sub_to_neighboring_pix_distance = closest_separation_from_pix_to_neighbor

            if sub_to_nearest_pix_distance <= sub_to_neighboring_pix_distance:
                sub_to_pix[sub_index] = nearest_pix_pixel_index
                break
            else:
                nearest_pix_pixel_index = neighboring_pix_pixel_index

    return sub_to_pix

Compute the mappings between a set of sub - grid pixels and pixelization pixels using information on \ how the regular pixels hosting each sub - pixel map to their closest pixelization pixel on the image - plane pix - grid \ and the pixelization s pixel centres .


def luminosity_within_circle_in_units(self, radius: dim.Length, unit_luminosity='eps', kpc_per_arcsec=None,
                                          exposure_time=None):
        """Integrate the light profile to compute the total luminosity within a circle of specified radius. This is \
        centred on the light profile's centre.

        The following units for mass can be specified and output:

        - Electrons per second (default) - 'eps'.
        - Counts - 'counts' (multiplies the luminosity in electrons per second by the exposure time).

        Parameters
        ----------
        radius : float
            The radius of the circle to compute the dimensionless mass within.
        unit_luminosity : str
            The units the luminosity is returned in (eps | counts).
        exposure_time : float or None
            The exposure time of the observation, which converts luminosity from electrons per second units to counts.
        """

        if not isinstance(radius, dim.Length):
            radius = dim.Length(value=radius, unit_length='arcsec')

        profile = self.new_profile_with_units_converted(unit_length=radius.unit_length, unit_luminosity=unit_luminosity,
                                                        kpc_per_arcsec=kpc_per_arcsec, exposure_time=exposure_time)

        luminosity = quad(profile.luminosity_integral, a=0.0, b=radius, args=(1.0,))[0]
        return dim.Luminosity(luminosity, unit_luminosity)

def luminosity_within_circle_in_units ( self , radius : dim . Length , unit_luminosity = 'eps' , kpc_per_arcsec = None , exposure_time = None ) : if not isinstance ( radius , dim . Length ) : radius = dim . Length ( value = radius , unit_length = 'arcsec' ) profile = self . new_profile_with_units_converted ( unit_length = radius . unit_length , unit_luminosity = unit_luminosity , kpc_per_arcsec = kpc_per_arcsec , exposure_time = exposure_time ) luminosity = quad ( profile . luminosity_integral , a = 0.0 , b = radius , args = ( 1.0 , ) ) [ 0 ] return dim . Luminosity ( luminosity , unit_luminosity )

Integrate the light profile to compute the total luminosity within a circle of specified radius . This is \ centred on the light profile s centre .


def luminosity_within_ellipse_in_units(self, major_axis, unit_luminosity='eps', kpc_per_arcsec=None,
                                           exposure_time=None):
        """Integrate the light profiles to compute the total luminosity within an ellipse of specified major axis. \
        This is centred on the light profile's centre.

        The following units for mass can be specified and output:

        - Electrons per second (default) - 'eps'.
        - Counts - 'counts' (multiplies the luminosity in electrons per second by the exposure time).

        Parameters
        ----------
        major_axis : float
            The major-axis radius of the ellipse.
        unit_luminosity : str
            The units the luminosity is returned in (eps | counts).
        exposure_time : float or None
            The exposure time of the observation, which converts luminosity from electrons per second units to counts.
        """

        if not isinstance(major_axis, dim.Length):
            major_axis = dim.Length(major_axis, 'arcsec')

        profile = self.new_profile_with_units_converted(unit_length=major_axis.unit_length,
                                                        unit_luminosity=unit_luminosity,
                                                        kpc_per_arcsec=kpc_per_arcsec, exposure_time=exposure_time)
        luminosity = quad(profile.luminosity_integral, a=0.0, b=major_axis, args=(self.axis_ratio,))[0]
        return dim.Luminosity(luminosity, unit_luminosity)

def luminosity_within_ellipse_in_units ( self , major_axis , unit_luminosity = 'eps' , kpc_per_arcsec = None , exposure_time = None ) : if not isinstance ( major_axis , dim . Length ) : major_axis = dim . Length ( major_axis , 'arcsec' ) profile = self . new_profile_with_units_converted ( unit_length = major_axis . unit_length , unit_luminosity = unit_luminosity , kpc_per_arcsec = kpc_per_arcsec , exposure_time = exposure_time ) luminosity = quad ( profile . luminosity_integral , a = 0.0 , b = major_axis , args = ( self . axis_ratio , ) ) [ 0 ] return dim . Luminosity ( luminosity , unit_luminosity )

Integrate the light profiles to compute the total luminosity within an ellipse of specified major axis . \ This is centred on the light profile s centre .


def luminosity_integral(self, x, axis_ratio):
        """Routine to integrate the luminosity of an elliptical light profile.

        The axis ratio is set to 1.0 for computing the luminosity within a circle"""
        r = x * axis_ratio
        return 2 * np.pi * r * self.intensities_from_grid_radii(x)

def luminosity_integral ( self , x , axis_ratio ) : r = x * axis_ratio return 2 * np . pi * r * self . intensities_from_grid_radii ( x )

Routine to integrate the luminosity of an elliptical light profile .


def intensities_from_grid_radii(self, grid_radii):
        """Calculate the intensity of the Gaussian light profile on a grid of radial coordinates.

        Parameters
        ----------
        grid_radii : float
            The radial distance from the centre of the profile. for each coordinate on the grid.
        """
        return np.multiply(np.divide(self.intensity, self.sigma * np.sqrt(2.0 * np.pi)),
                           np.exp(-0.5 * np.square(np.divide(grid_radii, self.sigma))))

def intensities_from_grid_radii ( self , grid_radii ) : return np . multiply ( np . divide ( self . intensity , self . sigma * np . sqrt ( 2.0 * np . pi ) ) , np . exp ( - 0.5 * np . square ( np . divide ( grid_radii , self . sigma ) ) ) )

Calculate the intensity of the Gaussian light profile on a grid of radial coordinates .


def intensities_from_grid_radii(self, grid_radii):
        """
        Calculate the intensity of the Sersic light profile on a grid of radial coordinates.

        Parameters
        ----------
        grid_radii : float
            The radial distance from the centre of the profile. for each coordinate on the grid.
        """
        np.seterr(all='ignore')
        return np.multiply(self.intensity, np.exp(
            np.multiply(-self.sersic_constant,
                        np.add(np.power(np.divide(grid_radii, self.effective_radius), 1. / self.sersic_index), -1))))

def intensities_from_grid_radii ( self , grid_radii ) : np . seterr ( all = 'ignore' ) return np . multiply ( self . intensity , np . exp ( np . multiply ( - self . sersic_constant , np . add ( np . power ( np . divide ( grid_radii , self . effective_radius ) , 1. / self . sersic_index ) , - 1 ) ) ) )

Calculate the intensity of the Sersic light profile on a grid of radial coordinates .


def intensities_from_grid_radii(self, grid_radii):
        """Calculate the intensity of the cored-Sersic light profile on a grid of radial coordinates.

        Parameters
        ----------
        grid_radii : float
            The radial distance from the centre of the profile. for each coordinate on the grid.
        """
        return np.multiply(np.multiply(self.intensity_prime, np.power(
            np.add(1, np.power(np.divide(self.radius_break, grid_radii), self.alpha)), (self.gamma / self.alpha))),
                           np.exp(np.multiply(-self.sersic_constant,
                                              (np.power(np.divide(np.add(np.power(grid_radii, self.alpha), (
                                                      self.radius_break ** self.alpha)),
                                                                  (self.effective_radius ** self.alpha)), (
                                                                1.0 / (self.alpha * self.sersic_index)))))))

def intensities_from_grid_radii ( self , grid_radii ) : return np . multiply ( np . multiply ( self . intensity_prime , np . power ( np . add ( 1 , np . power ( np . divide ( self . radius_break , grid_radii ) , self . alpha ) ) , ( self . gamma / self . alpha ) ) ) , np . exp ( np . multiply ( - self . sersic_constant , ( np . power ( np . divide ( np . add ( np . power ( grid_radii , self . alpha ) , ( self . radius_break ** self . alpha ) ) , ( self . effective_radius ** self . alpha ) ) , ( 1.0 / ( self . alpha * self . sersic_index ) ) ) ) ) ) )

Calculate the intensity of the cored - Sersic light profile on a grid of radial coordinates .


def luminosities_of_galaxies_within_circles_in_units(self, radius : dim.Length, unit_luminosity='eps', exposure_time=None):
        """Compute the total luminosity of all galaxies in this plane within a circle of specified radius.

        See *galaxy.light_within_circle* and *light_profiles.light_within_circle* for details \
        of how this is performed.

        Parameters
        ----------
        radius : float
            The radius of the circle to compute the dimensionless mass within.
        units_luminosity : str
            The units the luminosity is returned in (eps | counts).
        exposure_time : float
            The exposure time of the observation, which converts luminosity from electrons per second units to counts.
        """
        return list(map(lambda galaxy: galaxy.luminosity_within_circle_in_units(
            radius=radius, unit_luminosity=unit_luminosity, kpc_per_arcsec=self.kpc_per_arcsec,
            exposure_time=exposure_time),
                        self.galaxies))

Compute the total luminosity of all galaxies in this plane within a circle of specified radius .


def luminosities_of_galaxies_within_ellipses_in_units(self, major_axis : dim.Length, unit_luminosity='eps',
                                                          exposure_time=None):
        """
        Compute the total luminosity of all galaxies in this plane within a ellipse of specified major-axis.

        The value returned by this integral is dimensionless, and a conversion factor can be specified to convert it \
        to a physical value (e.g. the photometric zeropoint).

        See *galaxy.light_within_ellipse* and *light_profiles.light_within_ellipse* for details
        of how this is performed.

        Parameters
        ----------
        major_axis : float
            The major-axis radius of the ellipse.
        units_luminosity : str
            The units the luminosity is returned in (eps | counts).
        exposure_time : float
            The exposure time of the observation, which converts luminosity from electrons per second units to counts.
        """
        return list(map(lambda galaxy: galaxy.luminosity_within_ellipse_in_units(
            major_axis=major_axis, unit_luminosity=unit_luminosity, kpc_per_arcsec=self.kpc_per_arcsec,
            exposure_time=exposure_time),
                        self.galaxies))

Compute the total luminosity of all galaxies in this plane within a ellipse of specified major - axis .


def masses_of_galaxies_within_circles_in_units(self, radius : dim.Length, unit_mass='angular',
                                                   critical_surface_density=None):
        """Compute the total mass of all galaxies in this plane within a circle of specified radius.

        See *galaxy.angular_mass_within_circle* and *mass_profiles.angular_mass_within_circle* for details
        of how this is performed.

        Parameters
        ----------
        radius : float
            The radius of the circle to compute the dimensionless mass within.
        units_mass : str
            The units the mass is returned in (angular | solMass).
        critical_surface_density : float
            The critical surface mass density of the strong lens configuration, which converts mass from angulalr \
            units to physical units (e.g. solar masses).
        """
        return list(map(lambda galaxy: galaxy.mass_within_circle_in_units(
                        radius=radius, unit_mass=unit_mass, kpc_per_arcsec=self.kpc_per_arcsec,
                        critical_surface_density=critical_surface_density),
                        self.galaxies))

Compute the total mass of all galaxies in this plane within a circle of specified radius .


def masses_of_galaxies_within_ellipses_in_units(self, major_axis : dim.Length, unit_mass='angular',
                                                    critical_surface_density=None):
        """Compute the total mass of all galaxies in this plane within a ellipse of specified major-axis.

        See *galaxy.angular_mass_within_ellipse* and *mass_profiles.angular_mass_within_ellipse* for details \
        of how this is performed.

        Parameters
        ----------
        major_axis : float
            The major-axis radius of the ellipse.
        units_luminosity : str
            The units the luminosity is returned in (eps | counts).
        exposure_time : float
            The exposure time of the observation, which converts luminosity from electrons per second units to counts.
        """
        return list(map(lambda galaxy: galaxy.mass_within_ellipse_in_units(
                        major_axis=major_axis, unit_mass=unit_mass, kpc_per_arcsec=self.kpc_per_arcsec,
                        critical_surface_density=critical_surface_density),
                        self.galaxies))

Compute the total mass of all galaxies in this plane within a ellipse of specified major - axis .


def trace_grid_stack_to_next_plane(self):
        """Trace this plane's grid_stacks to the next plane, using its deflection angles."""

        def minus(grid, deflections):
            return grid - deflections

        return self.grid_stack.map_function(minus, self.deflection_stack)

def trace_grid_stack_to_next_plane ( self ) : def minus ( grid , deflections ) : return grid - deflections return self . grid_stack . map_function ( minus , self . deflection_stack )

Trace this plane s grid_stacks to the next plane using its deflection angles .


def yticks(self):
        """Compute the yticks labels of this grid_stack, used for plotting the y-axis ticks when visualizing an image \
        """
        return np.linspace(np.amin(self.grid_stack.regular[:, 0]), np.amax(self.grid_stack.regular[:, 0]), 4)

def yticks ( self ) : return np . linspace ( np . amin ( self . grid_stack . regular [ : , 0 ] ) , np . amax ( self . grid_stack . regular [ : , 0 ] ) , 4 )

Compute the yticks labels of this grid_stack used for plotting the y - axis ticks when visualizing an image \


def xticks(self):
        """Compute the xticks labels of this grid_stack, used for plotting the x-axis ticks when visualizing an \
        image"""
        return np.linspace(np.amin(self.grid_stack.regular[:, 1]), np.amax(self.grid_stack.regular[:, 1]), 4)

def xticks ( self ) : return np . linspace ( np . amin ( self . grid_stack . regular [ : , 1 ] ) , np . amax ( self . grid_stack . regular [ : , 1 ] ) , 4 )

Compute the xticks labels of this grid_stack used for plotting the x - axis ticks when visualizing an \ image


def unmasked_blurred_image_of_galaxies_from_psf(self, padded_grid_stack, psf):
        """This is a utility function for the function above, which performs the iteration over each plane's galaxies \
        and computes each galaxy's unmasked blurred image.

        Parameters
        ----------
        padded_grid_stack
        psf : ccd.PSF
            The PSF of the image used for convolution.
        """
        return [padded_grid_stack.unmasked_blurred_image_from_psf_and_unmasked_image(
            psf, image) if not galaxy.has_pixelization else None for galaxy, image in
                zip(self.galaxies, self.image_plane_image_1d_of_galaxies)]

This is a utility function for the function above which performs the iteration over each plane s galaxies \ and computes each galaxy s unmasked blurred image .


def trace_to_next_plane(self):
        """Trace the positions to the next plane."""
        return list(map(lambda positions, deflections: np.subtract(positions, deflections),
                        self.positions, self.deflections))

def trace_to_next_plane ( self ) : return list ( map ( lambda positions , deflections : np . subtract ( positions , deflections ) , self . positions , self . deflections ) )


def single_value(cls, value, shape, pixel_scale, origin=(0.0, 0.0)):
        """
        Creates an instance of Array and fills it with a single value

        Parameters
        ----------
        value: float
            The value with which the array should be filled
        shape: (int, int)
            The shape of the array
        pixel_scale: float
            The scale of a pixel in arc seconds

        Returns
        -------
        array: ScaledSquarePixelArray
            An array filled with a single value
        """
        array = np.ones(shape) * value
        return cls(array, pixel_scale, origin)

def single_value ( cls , value , shape , pixel_scale , origin = ( 0.0 , 0.0 ) ) : array = np . ones ( shape ) * value return cls ( array , pixel_scale , origin )


def zoomed_scaled_array_around_mask(self, mask, buffer=1):
        """Extract the 2D region of an array corresponding to the rectangle encompassing all unmasked values.

        This is used to extract and visualize only the region of an image that is used in an analysis.

        Parameters
        ----------
        mask : mask.Mask
            The mask around which the scaled array is extracted.
        buffer : int
            The buffer of pixels around the extraction.
        """
        return self.new_with_array(array=array_util.extracted_array_2d_from_array_2d_and_coordinates(
            array_2d=self,  y0=mask.zoom_region[0]-buffer, y1=mask.zoom_region[1]+buffer,
            x0=mask.zoom_region[2]-buffer, x1=mask.zoom_region[3]+buffer))

Extract the 2D region of an array corresponding to the rectangle encompassing all unmasked values .


def resized_scaled_array_from_array(self, new_shape, new_centre_pixels=None, new_centre_arcsec=None):
        """resized the array to a new shape and at a new origin.

        Parameters
        -----------
        new_shape : (int, int)
            The new two-dimensional shape of the array.
        """
        if new_centre_pixels is None and new_centre_arcsec is None:
            new_centre = (-1, -1)  # In Numba, the input origin must be the same image type as the origin, thus we cannot
            # pass 'None' and instead use the tuple (-1, -1).
        elif new_centre_pixels is not None and new_centre_arcsec is None:
            new_centre = new_centre_pixels
        elif new_centre_pixels is None and new_centre_arcsec is not None:
            new_centre = self.arc_second_coordinates_to_pixel_coordinates(arc_second_coordinates=new_centre_arcsec)
        else:
            raise exc.DataException('You have supplied two centres (pixels and arc-seconds) to the resize scaled'
                                       'array function')

        return self.new_with_array(array=array_util.resized_array_2d_from_array_2d_and_resized_shape(
            array_2d=self, resized_shape=new_shape, origin=new_centre))

resized the array to a new shape and at a new origin .


def fit_lens_data_with_sensitivity_tracers(lens_data, tracer_normal, tracer_sensitive):
    """Fit lens data with a normal tracer and sensitivity tracer, to determine our sensitivity to a selection of \
    galaxy components. This factory automatically determines the type of fit based on the properties of the galaxies \
    in the tracers.

    Parameters
    -----------
    lens_data : lens_data.LensData or lens_data.LensDataHyper
        The lens-images that is fitted.
    tracer_normal : ray_tracing.AbstractTracer
        A tracer whose galaxies have the same model components (e.g. light profiles, mass profiles) as the \
        lens data that we are fitting.
    tracer_sensitive : ray_tracing.AbstractTracerNonStack
        A tracer whose galaxies have the same model components (e.g. light profiles, mass profiles) as the \
        lens data that we are fitting, but also addition components (e.g. mass clumps) which we measure \
        how sensitive we are too.
    """

    if (tracer_normal.has_light_profile and tracer_sensitive.has_light_profile) and \
            (not tracer_normal.has_pixelization and not tracer_sensitive.has_pixelization):
        return SensitivityProfileFit(lens_data=lens_data, tracer_normal=tracer_normal,
                                     tracer_sensitive=tracer_sensitive)

    elif (not tracer_normal.has_light_profile and not tracer_sensitive.has_light_profile) and \
            (tracer_normal.has_pixelization and tracer_sensitive.has_pixelization):
        return SensitivityInversionFit(lens_data=lens_data, tracer_normal=tracer_normal,
                                     tracer_sensitive=tracer_sensitive)
    else:

        raise exc.FittingException('The sensitivity_fit routine did not call a SensitivityFit class - check the '
                                   'properties of the tracers')

Fit lens data with a normal tracer and sensitivity tracer to determine our sensitivity to a selection of \ galaxy components . This factory automatically determines the type of fit based on the properties of the galaxies \ in the tracers .


def unmasked_for_shape_and_pixel_scale(cls, shape, pixel_scale, invert=False):
        """Setup a mask where all pixels are unmasked.

        Parameters
        ----------
        shape : (int, int)
            The (y,x) shape of the mask in units of pixels.
        pixel_scale: float
            The arc-second to pixel conversion factor of each pixel.
        """
        mask = np.full(tuple(map(lambda d: int(d), shape)), False)
        if invert: mask = np.invert(mask)
        return cls(array=mask, pixel_scale=pixel_scale)

def unmasked_for_shape_and_pixel_scale ( cls , shape , pixel_scale , invert = False ) : mask = np . full ( tuple ( map ( lambda d : int ( d ) , shape ) ) , False ) if invert : mask = np . invert ( mask ) return cls ( array = mask , pixel_scale = pixel_scale )

Setup a mask where all pixels are unmasked .


def circular(cls, shape, pixel_scale, radius_arcsec, centre=(0., 0.), invert=False):
        """Setup a mask where unmasked pixels are within a circle of an input arc second radius and centre.

        Parameters
        ----------
        shape: (int, int)
            The (y,x) shape of the mask in units of pixels.
        pixel_scale: float
            The arc-second to pixel conversion factor of each pixel.
        radius_arcsec : float
            The radius (in arc seconds) of the circle within which pixels unmasked.
        centre: (float, float)
            The centre of the circle used to mask pixels.
        """
        mask = mask_util.mask_circular_from_shape_pixel_scale_and_radius(shape, pixel_scale, radius_arcsec,
                                                                         centre)
        if invert: mask = np.invert(mask)
        return cls(array=mask.astype('bool'), pixel_scale=pixel_scale)

Setup a mask where unmasked pixels are within a circle of an input arc second radius and centre .


def circular_annular(cls, shape, pixel_scale, inner_radius_arcsec, outer_radius_arcsec, centre=(0., 0.),
                         invert=False):
        """Setup a mask where unmasked pixels are within an annulus of input inner and outer arc second radii and \
         centre.

        Parameters
        ----------
        shape : (int, int)
            The (y,x) shape of the mask in units of pixels.
        pixel_scale: float
            The arc-second to pixel conversion factor of each pixel.
        inner_radius_arcsec : float
            The radius (in arc seconds) of the inner circle outside of which pixels are unmasked.
        outer_radius_arcsec : float
            The radius (in arc seconds) of the outer circle within which pixels are unmasked.
        centre: (float, float)
            The centre of the annulus used to mask pixels.
        """
        mask = mask_util.mask_circular_annular_from_shape_pixel_scale_and_radii(shape, pixel_scale, inner_radius_arcsec,
                                                                                outer_radius_arcsec, centre)
        if invert: mask = np.invert(mask)
        return cls(array=mask.astype('bool'), pixel_scale=pixel_scale)

Setup a mask where unmasked pixels are within an annulus of input inner and outer arc second radii and \ centre .


def circular_anti_annular(cls, shape, pixel_scale, inner_radius_arcsec, outer_radius_arcsec, outer_radius_2_arcsec,
                              centre=(0., 0.), invert=False):
        """Setup a mask where unmasked pixels are outside an annulus of input inner and outer arc second radii, but \
        within a second outer radius, and at a given centre.

        This mask there has two distinct unmasked regions (an inner circle and outer annulus), with an inner annulus \
        of masked pixels.

        Parameters
        ----------
        shape : (int, int)
            The (y,x) shape of the mask in units of pixels.
        pixel_scale: float
            The arc-second to pixel conversion factor of each pixel.
        inner_radius_arcsec : float
            The radius (in arc seconds) of the inner circle inside of which pixels are unmasked.
        outer_radius_arcsec : float
            The radius (in arc seconds) of the outer circle within which pixels are masked and outside of which they \
            are unmasked.
        outer_radius_2_arcsec : float
            The radius (in arc seconds) of the second outer circle within which pixels are unmasked and outside of \
            which they masked.
        centre: (float, float)
            The centre of the anti-annulus used to mask pixels.
        """
        mask = mask_util.mask_circular_anti_annular_from_shape_pixel_scale_and_radii(shape, pixel_scale, inner_radius_arcsec,
                                                                                outer_radius_arcsec,
                                                                                outer_radius_2_arcsec, centre)
        if invert: mask = np.invert(mask)
        return cls(array=mask.astype('bool'), pixel_scale=pixel_scale)

Setup a mask where unmasked pixels are outside an annulus of input inner and outer arc second radii but \ within a second outer radius and at a given centre .


def elliptical(cls, shape, pixel_scale, major_axis_radius_arcsec, axis_ratio, phi, centre=(0., 0.),
                   invert=False):
        """ Setup a mask where unmasked pixels are within an ellipse of an input arc second major-axis and centre.

        Parameters
        ----------
        shape: (int, int)
            The (y,x) shape of the mask in units of pixels.
        pixel_scale: float
            The arc-second to pixel conversion factor of each pixel.
        major_axis_radius_arcsec : float
            The major-axis (in arc seconds) of the ellipse within which pixels are unmasked.
        axis_ratio : float
            The axis-ratio of the ellipse within which pixels are unmasked.
        phi : float
            The rotation angle of the ellipse within which pixels are unmasked, (counter-clockwise from the positive \
             x-axis).
        centre: (float, float)
            The centre of the ellipse used to mask pixels.
        """
        mask = mask_util.mask_elliptical_from_shape_pixel_scale_and_radius(shape, pixel_scale, major_axis_radius_arcsec,
                                                                          axis_ratio, phi, centre)
        if invert: mask = np.invert(mask)
        return cls(array=mask.astype('bool'), pixel_scale=pixel_scale)

Setup a mask where unmasked pixels are within an ellipse of an input arc second major - axis and centre .


def elliptical_annular(cls, shape, pixel_scale,inner_major_axis_radius_arcsec, inner_axis_ratio, inner_phi,
                           outer_major_axis_radius_arcsec, outer_axis_ratio, outer_phi, centre=(0.0, 0.0),
                           invert=False):
        """Setup a mask where unmasked pixels are within an elliptical annulus of input inner and outer arc second \
        major-axis and centre.

        Parameters
        ----------
        shape: (int, int)
            The (y,x) shape of the mask in units of pixels.
        pixel_scale: float
            The arc-second to pixel conversion factor of each pixel.
        inner_major_axis_radius_arcsec : float
            The major-axis (in arc seconds) of the inner ellipse within which pixels are masked.
        inner_axis_ratio : float
            The axis-ratio of the inner ellipse within which pixels are masked.
        inner_phi : float
            The rotation angle of the inner ellipse within which pixels are masked, (counter-clockwise from the \
            positive x-axis).
        outer_major_axis_radius_arcsec : float
            The major-axis (in arc seconds) of the outer ellipse within which pixels are unmasked.
        outer_axis_ratio : float
            The axis-ratio of the outer ellipse within which pixels are unmasked.
        outer_phi : float
            The rotation angle of the outer ellipse within which pixels are unmasked, (counter-clockwise from the \
            positive x-axis).
        centre: (float, float)
            The centre of the elliptical annuli used to mask pixels.
        """
        mask = mask_util.mask_elliptical_annular_from_shape_pixel_scale_and_radius(shape, pixel_scale,
                           inner_major_axis_radius_arcsec, inner_axis_ratio, inner_phi,
                           outer_major_axis_radius_arcsec, outer_axis_ratio, outer_phi, centre)
        if invert: mask = np.invert(mask)
        return cls(array=mask.astype('bool'), pixel_scale=pixel_scale)

Setup a mask where unmasked pixels are within an elliptical annulus of input inner and outer arc second \ major - axis and centre .


def zoom_region(self):
        """The zoomed rectangular region corresponding to the square encompassing all unmasked values.

        This is used to zoom in on the region of an image that is used in an analysis for visualization."""

        # Have to convert mask to bool for invert function to work.
        where = np.array(np.where(np.invert(self.astype('bool'))))
        y0, x0 = np.amin(where, axis=1)
        y1, x1 = np.amax(where, axis=1)
        return [y0, y1+1, x0, x1+1]

The zoomed rectangular region corresponding to the square encompassing all unmasked values .


def instance_for_arguments(self, arguments):
        """
        Create an instance of the associated class for a set of arguments

        Parameters
        ----------
        arguments: {Prior: value}
            Dictionary mapping_matrix priors to attribute analysis_path and value pairs

        Returns
        -------
            An instance of the class
        """
        profiles = {**{key: value.instance_for_arguments(arguments)
                       for key, value
                       in self.profile_prior_model_dict.items()}, **self.constant_profile_dict}

        try:
            redshift = self.redshift.instance_for_arguments(arguments)
        except AttributeError:
            redshift = self.redshift
        pixelization = self.pixelization.instance_for_arguments(arguments) \
            if isinstance(self.pixelization, pm.PriorModel) \
            else self.pixelization
        regularization = self.regularization.instance_for_arguments(arguments) \
            if isinstance(self.regularization, pm.PriorModel) \
            else self.regularization
        hyper_galaxy = self.hyper_galaxy.instance_for_arguments(arguments) \
            if isinstance(self.hyper_galaxy, pm.PriorModel) \
            else self.hyper_galaxy

        return galaxy.Galaxy(redshift=redshift, pixelization=pixelization, regularization=regularization,
                             hyper_galaxy=hyper_galaxy, **profiles)

def instance_for_arguments ( self , arguments ) : profiles = { * * { key : value . instance_for_arguments ( arguments ) for key , value in self . profile_prior_model_dict . items ( ) } , * * self . constant_profile_dict } try : redshift = self . redshift . instance_for_arguments ( arguments ) except AttributeError : redshift = self . redshift pixelization = self . pixelization . instance_for_arguments ( arguments ) if isinstance ( self . pixelization , pm . PriorModel ) else self . pixelization regularization = self . regularization . instance_for_arguments ( arguments ) if isinstance ( self . regularization , pm . PriorModel ) else self . regularization hyper_galaxy = self . hyper_galaxy . instance_for_arguments ( arguments ) if isinstance ( self . hyper_galaxy , pm . PriorModel ) else self . hyper_galaxy return galaxy . Galaxy ( redshift = redshift , pixelization = pixelization , regularization = regularization , hyper_galaxy = hyper_galaxy , * * profiles )


def gaussian_prior_model_for_arguments(self, arguments):
        """
        Create a new galaxy prior from a set of arguments, replacing the priors of some of this galaxy prior's prior
        models with new arguments.

        Parameters
        ----------
        arguments: dict
            A dictionary mapping_matrix between old priors and their replacements.

        Returns
        -------
        new_model: GalaxyModel
            A model with some or all priors replaced.
        """
        new_model = copy.deepcopy(self)

        for key, value in filter(lambda t: isinstance(t[1], pm.PriorModel), self.__dict__.items()):
            setattr(new_model, key, value.gaussian_prior_model_for_arguments(arguments))

        return new_model

def gaussian_prior_model_for_arguments ( self , arguments ) : new_model = copy . deepcopy ( self ) for key , value in filter ( lambda t : isinstance ( t [ 1 ] , pm . PriorModel ) , self . __dict__ . items ( ) ) : setattr ( new_model , key , value . gaussian_prior_model_for_arguments ( arguments ) ) return new_model

Create a new galaxy prior from a set of arguments replacing the priors of some of this galaxy prior s prior models with new arguments .


def plot_image(
        image, plot_origin=True, mask=None, extract_array_from_mask=False, zoom_around_mask=False,
        should_plot_border=False, positions=None, as_subplot=False,
        units='arcsec', kpc_per_arcsec=None, figsize=(7, 7), aspect='square',
        cmap='jet', norm='linear', norm_min=None, norm_max=None, linthresh=0.05, linscale=0.01,
        cb_ticksize=10, cb_fraction=0.047, cb_pad=0.01, cb_tick_values=None, cb_tick_labels=None,
        title='Image', titlesize=16, xlabelsize=16, ylabelsize=16, xyticksize=16,
        mask_pointsize=10, position_pointsize=30, grid_pointsize=1,
        output_path=None, output_format='show', output_filename='image'):
    """Plot the observed image of the ccd data.

    Set *autolens.data.array.plotters.array_plotters* for a description of all input parameters not described below.

    Parameters
    -----------
    image : ScaledSquarePixelArray
        The image of the data.
    plot_origin : True
        If true, the origin of the data's coordinate system is plotted as a 'x'.
    image_plane_pix_grid : ndarray or data.array.grid_stacks.PixGrid
        If an adaptive pixelization whose pixels are formed by tracing pixels from the data, this plots those pixels \
        over the immage.
    """
    origin = get_origin(array=image, plot_origin=plot_origin)

    array_plotters.plot_array(
        array=image, origin=origin, mask=mask, extract_array_from_mask=extract_array_from_mask,
        zoom_around_mask=zoom_around_mask,
        should_plot_border=should_plot_border, positions=positions, as_subplot=as_subplot,
        units=units, kpc_per_arcsec=kpc_per_arcsec, figsize=figsize, aspect=aspect,
        cmap=cmap, norm=norm, norm_min=norm_min, norm_max=norm_max, linthresh=linthresh, linscale=linscale,
        cb_ticksize=cb_ticksize, cb_fraction=cb_fraction, cb_pad=cb_pad,
        cb_tick_values=cb_tick_values, cb_tick_labels=cb_tick_labels,
        title=title, titlesize=titlesize, xlabelsize=xlabelsize, ylabelsize=ylabelsize, xyticksize=xyticksize,
        mask_pointsize=mask_pointsize, position_pointsize=position_pointsize, grid_pointsize=grid_pointsize,
        output_path=output_path, output_format=output_format, output_filename=output_filename)

def plot_image ( image , plot_origin = True , mask = None , extract_array_from_mask = False , zoom_around_mask = False , should_plot_border = False , positions = None , as_subplot = False , units = 'arcsec' , kpc_per_arcsec = None , figsize = ( 7 , 7 ) , aspect = 'square' , cmap = 'jet' , norm = 'linear' , norm_min = None , norm_max = None , linthresh = 0.05 , linscale = 0.01 , cb_ticksize = 10 , cb_fraction = 0.047 , cb_pad = 0.01 , cb_tick_values = None , cb_tick_labels = None , title = 'Image' , titlesize = 16 , xlabelsize = 16 , ylabelsize = 16 , xyticksize = 16 , mask_pointsize = 10 , position_pointsize = 30 , grid_pointsize = 1 , output_path = None , output_format = 'show' , output_filename = 'image' ) : origin = get_origin ( array = image , plot_origin = plot_origin ) array_plotters . plot_array ( array = image , origin = origin , mask = mask , extract_array_from_mask = extract_array_from_mask , zoom_around_mask = zoom_around_mask , should_plot_border = should_plot_border , positions = positions , as_subplot = as_subplot , units = units , kpc_per_arcsec = kpc_per_arcsec , figsize = figsize , aspect = aspect , cmap = cmap , norm = norm , norm_min = norm_min , norm_max = norm_max , linthresh = linthresh , linscale = linscale , cb_ticksize = cb_ticksize , cb_fraction = cb_fraction , cb_pad = cb_pad , cb_tick_values = cb_tick_values , cb_tick_labels = cb_tick_labels , title = title , titlesize = titlesize , xlabelsize = xlabelsize , ylabelsize = ylabelsize , xyticksize = xyticksize , mask_pointsize = mask_pointsize , position_pointsize = position_pointsize , grid_pointsize = grid_pointsize , output_path = output_path , output_format = output_format , output_filename = output_filename )

Plot the observed image of the ccd data .


def norm_and_check(source_tree, requested):
    """Normalise and check a backend path.

    Ensure that the requested backend path is specified as a relative path,
    and resolves to a location under the given source tree.

    Return an absolute version of the requested path.
    """
    if os.path.isabs(requested):
        raise ValueError("paths must be relative")

    abs_source = os.path.abspath(source_tree)
    abs_requested = os.path.normpath(os.path.join(abs_source, requested))
    # We have to use commonprefix for Python 2.7 compatibility. So we
    # normalise case to avoid problems because commonprefix is a character
    # based comparison :-(
    norm_source = os.path.normcase(abs_source)
    norm_requested = os.path.normcase(abs_requested)
    if os.path.commonprefix([norm_source, norm_requested]) != norm_source:
        raise ValueError("paths must be inside source tree")

    return abs_requested


def contained_in(filename, directory):
    """Test if a file is located within the given directory."""
    filename = os.path.normcase(os.path.abspath(filename))
    directory = os.path.normcase(os.path.abspath(directory))
    return os.path.commonprefix([filename, directory]) == directory

def contained_in ( filename , directory ) : filename = os . path . normcase ( os . path . abspath ( filename ) ) directory = os . path . normcase ( os . path . abspath ( directory ) ) return os . path . commonprefix ( [ filename , directory ] ) == directory

Test if a file is located within the given directory .


def _build_backend():
    """Find and load the build backend"""
    # Add in-tree backend directories to the front of sys.path.
    backend_path = os.environ.get('PEP517_BACKEND_PATH')
    if backend_path:
        extra_pathitems = backend_path.split(os.pathsep)
        sys.path[:0] = extra_pathitems

    ep = os.environ['PEP517_BUILD_BACKEND']
    mod_path, _, obj_path = ep.partition(':')
    try:
        obj = import_module(mod_path)
    except ImportError:
        raise BackendUnavailable(traceback.format_exc())

    if backend_path:
        if not any(
            contained_in(obj.__file__, path)
            for path in extra_pathitems
        ):
            raise BackendInvalid("Backend was not loaded from backend-path")

    if obj_path:
        for path_part in obj_path.split('.'):
            obj = getattr(obj, path_part)
    return obj


def build_sdist(sdist_directory, config_settings):
    """Invoke the mandatory build_sdist hook."""
    backend = _build_backend()
    try:
        return backend.build_sdist(sdist_directory, config_settings)
    except getattr(backend, 'UnsupportedOperation', _DummyException):
        raise GotUnsupportedOperation(traceback.format_exc())

def build_sdist ( sdist_directory , config_settings ) : backend = _build_backend ( ) try : return backend . build_sdist ( sdist_directory , config_settings ) except getattr ( backend , 'UnsupportedOperation' , _DummyException ) : raise GotUnsupportedOperation ( traceback . format_exc ( ) )


def get(self, *args, **kwargs):

        """
        An interface for get requests that handles errors more gracefully to
        prevent data loss
        """

        try:
            req_func = self.session.get if self.session else requests.get
            req = req_func(*args, **kwargs)
            req.raise_for_status()
            self.failed_last = False
            return req

        except requests.exceptions.RequestException as e:
            self.log_error(e)
            for i in range(1, self.num_retries):
                sleep_time = self.retry_rate * i
                self.log_function("Retrying in %s seconds" % sleep_time)
                self._sleep(sleep_time)
                try:
                    req = requests.get(*args, **kwargs)
                    req.raise_for_status()
                    self.log_function("New request successful")
                    return req
                except requests.exceptions.RequestException:
                    self.log_function("New request failed")

            # Allows for the api to ignore one potentially bad request
            if not self.failed_last:
                self.failed_last = True
                raise ApiError(e)
            else:
                raise FatalApiError(e)


def convert_frames_to_video(tar_file_path, output_path="output.mp4", framerate=60, overwrite=False):
    """
    Try to convert a tar file containing a sequence of frames saved by the
    meshcat viewer into a single video file.

    This relies on having `ffmpeg` installed on your system.
    """
    output_path = os.path.abspath(output_path)
    if os.path.isfile(output_path) and not overwrite:
        raise ValueError("The output path {:s} already exists. To overwrite that file, you can pass overwrite=True to this function.".format(output_path))
    with tempfile.TemporaryDirectory() as tmp_dir:
        with tarfile.open(tar_file_path) as tar:
            tar.extractall(tmp_dir)
        args = ["ffmpeg",
                "-r", str(framerate),
                "-i", r"%07d.png",
                "-vcodec", "libx264",
                "-preset", "slow",
                "-crf", "18"]
        if overwrite:
            args.append("-y")
        args.append(output_path)
        try:
            subprocess.check_call(args, cwd=tmp_dir)
        except subprocess.CalledProcessError as e:
            print("""
Could not call `ffmpeg` to convert your frames into a video.
If you want to convert the frames manually, you can extract the
.tar archive into a directory, cd to that directory, and run:
ffmpeg -r 60 -i %07d.png \\\n\t -vcodec libx264 \\\n\t -preset slow \\\n\t -crf 18 \\\n\t output.mp4
                """)
            raise
    print("Saved output as {:s}".format(output_path))
    return output_path

Try to convert a tar file containing a sequence of frames saved by the meshcat viewer into a single video file .


def toJSON(self):
        """Get a json dict of the attributes of this object."""
        return {"id": self.id,
                "compile": self.compile,
                "position": self.position,
                "version": self.version}

def toJSON ( self ) : return { "id" : self . id , "compile" : self . compile , "position" : self . position , "version" : self . version }

Get a json dict of the attributes of this object .


def from_string(cls, content, position=1, file_id=None):
        """
        Convenience method to create a file from a string.

        This file object's metadata will have the id 'inlined_input'.

        Inputs
        ------
        content -- the content of the file (a string).
        position -- (default 1) rank among all files of the model while parsing
            see FileMetadata
        file_id -- (default 'inlined_input') the file_id that will be used by
            kappa.
        """
        if file_id is None:
            file_id = 'inlined_input'
        return cls(FileMetadata(file_id, position), content)

def from_string ( cls , content , position = 1 , file_id = None ) : if file_id is None : file_id = 'inlined_input' return cls ( FileMetadata ( file_id , position ) , content )

Convenience method to create a file from a string .


def from_file(cls, fpath, position=1, file_id=None):
        """
        Convience method to create a kappa file object from a file on disk

        Inputs
        ------
        fpath -- path to the file on disk
        position -- (default 1) rank among all files of the model while parsing
            see FileMetadata
        file_id -- (default = fpath) the file_id that will be used by kappa.
        """
        if file_id is None:
            file_id = fpath
        with open(fpath) as f:
            code = f.read()
            file_content = str(code)
            file_metadata = FileMetadata(file_id, position)
            return cls(file_metadata, file_content)

def from_file ( cls , fpath , position = 1 , file_id = None ) : if file_id is None : file_id = fpath with open ( fpath ) as f : code = f . read ( ) file_content = str ( code ) file_metadata = FileMetadata ( file_id , position ) return cls ( file_metadata , file_content )


def _fix_docs(this_abc, child_class):
        """Make api method docs inheritted.

        Specifically, insepect.getdoc will return values inheritted from this
        abc for standardized api methods.
        """
        # After python 3.5, this is basically handled automatically
        if sys.version_info >= (3, 5):
            return child_class

        if not issubclass(child_class, this_abc):
            raise KappaError('Cannot fix docs of class that is not decendent.')

        # This method is modified from solution given in
        # https://stackoverflow.com/a/8101598/8863865
        for name, child_func in vars(child_class).items():
            if callable(child_func) and not child_func.__doc__:
                if name in this_abc.__abstractmethods__:
                    parent_func = getattr(this_abc, name)
                    child_func.__doc__ = parent_func.__doc__
        return child_class


def add_model_string(self, model_str, position=1, file_id=None):
        """Add a kappa model given in a string to the project."""
        if file_id is None:
            file_id = self.make_unique_id('inlined_input')
        ret_data = self.file_create(File.from_string(model_str, position,
                                                     file_id))
        return ret_data

def add_model_string ( self , model_str , position = 1 , file_id = None ) : if file_id is None : file_id = self . make_unique_id ( 'inlined_input' ) ret_data = self . file_create ( File . from_string ( model_str , position , file_id ) ) return ret_data

Add a kappa model given in a string to the project .


def add_model_file(self, model_fpath, position=1, file_id=None):
        """Add a kappa model from a file at given path to the project."""
        if file_id is None:
            file_id = self.make_unique_id('file_input')
        ret_data = self.file_create(File.from_file(model_fpath, position,
                                                   file_id))
        return ret_data

def add_model_file ( self , model_fpath , position = 1 , file_id = None ) : if file_id is None : file_id = self . make_unique_id ( 'file_input' ) ret_data = self . file_create ( File . from_file ( model_fpath , position , file_id ) ) return ret_data

Add a kappa model from a file at given path to the project .


def set_default_sim_param(self, *args, **kwargs):
        """Set the simulation default simulation parameters.

        You can pass one of two things in as input:
        - a kappa_common.SimulationParameter instance
        - the arguments and keyword argument to create such an instance.

        The parameters you specify will be used by default in simulations run
        by this client.
        """
        if len(args) is 1 and isinstance(args[0], SimulationParameter):
            self.__default_param = args[0]
        else:
            self.__default_param = SimulationParameter(*args, **kwargs)
        return

def set_default_sim_param ( self , * args , * * kwargs ) : if len ( args ) is 1 and isinstance ( args [ 0 ] , SimulationParameter ) : self . __default_param = args [ 0 ] else : self . __default_param = SimulationParameter ( * args , * * kwargs ) return

Set the simulation default simulation parameters .


def get_is_sim_running(self):
        """Check if the current simulation is running."""
        sim_info = self.simulation_info()
        try:
            progress_info = sim_info['simulation_info_progress']
            ret = progress_info['simulation_progress_is_running']
        except KeyError:  # Simulation has not been created.
            ret = False
        return ret

def
get_is_sim_running
(
self
)
:
sim_info
=
self
.
simulation_info
(
)
try
:
progress_info
=
sim_info
[
'simulation_info_progress'
]
ret
=
progress_info
[
'simulation_progress_is_running'
]
except
KeyError
:
# Simulation has not been created.
ret
=
False
return
ret

Check if the current simulation is running .


def wait_for_simulation_stop(self, timeout=None):
        """Block until the simulation is done or timeout seconds exceeded.

        If the simulation stops before timeout, siminfo is returned.
        """
        start = datetime.now()
        while self.get_is_sim_running():
            sleep(0.5)
            if timeout is not None:
                if (datetime.now() - start).seconds >= timeout:
                    ret = None
                    break
        else:
            ret = self.simulation_info()
        return ret

def wait_for_simulation_stop ( self , timeout = None ) : start = datetime . now ( ) while self . get_is_sim_running ( ) : sleep ( 0.5 ) if timeout is not None : if ( datetime . now ( ) - start ) . seconds >= timeout : ret = None break else : ret = self . simulation_info ( ) return ret

Block until the simulation is done or timeout seconds exceeded .


def in_out_check(self):
        """
        Checks the input and output to see if they are valid

        """
        devices = available_devices()
        if not self.in_idx in devices:
            raise OSError("Input device is unavailable")
        in_check = devices[self.in_idx]
        if not self.out_idx in devices:
            raise OSError("Output device is unavailable")
        out_check = devices[self.out_idx]
        if((in_check['inputs'] == 0) and (out_check['outputs']==0)):
            raise StandardError('Invalid input and output devices')
        elif(in_check['inputs'] == 0):
            raise ValueError('Selected input device has no inputs')
        elif(out_check['outputs'] == 0):
            raise ValueError('Selected output device has no outputs')
        return True

def
in_out_check
(
self
)
:
devices
=
available_devices
(
)
if
not
self
.
in_idx
in
devices
:
raise
OSError
(
"Input device is unavailable"
)
in_check
=
devices
[
self
.
in_idx
]
if
not
self
.
out_idx
in
devices
:
raise
OSError
(
"Output device is unavailable"
)
out_check
=
devices
[
self
.
out_idx
]
if
(
(
in_check
[
'inputs'
]
==
0
)
and
(
out_check
[
'outputs'
]
==
0
)
)
:
raise
StandardError
(
'Invalid input and output devices'
)
elif
(
in_check
[
'inputs'
]
==
0
)
:
raise
ValueError
(
'Selected input device has no inputs'
)
elif
(
out_check
[
'outputs'
]
==
0
)
:
raise
ValueError
(
'Selected output device has no outputs'
)
return
True


def DSP_capture_add_samples(self,new_data):
        """
        Append new samples to the data_capture array and increment the sample counter
        If length reaches Tcapture, then the newest samples will be kept. If Tcapture = 0
        then new values are not appended to the data_capture array.

        """
        self.capture_sample_count += len(new_data)
        if self.Tcapture > 0:
            self.data_capture = np.hstack((self.data_capture,new_data))
            if (self.Tcapture > 0) and (len(self.data_capture) > self.Ncapture):
                self.data_capture = self.data_capture[-self.Ncapture:]

def DSP_capture_add_samples ( self , new_data ) : self . capture_sample_count += len ( new_data ) if self . Tcapture > 0 : self . data_capture = np . hstack ( ( self . data_capture , new_data ) ) if ( self . Tcapture > 0 ) and ( len ( self . data_capture ) > self . Ncapture ) : self . data_capture = self . data_capture [ - self . Ncapture : ]

Append new samples to the data_capture array and increment the sample counter If length reaches Tcapture then the newest samples will be kept . If Tcapture = 0 then new values are not appended to the data_capture array .


def DSP_capture_add_samples_stereo(self,new_data_left,new_data_right):
        """
        Append new samples to the data_capture_left array and the data_capture_right
        array and increment the sample counter. If length reaches Tcapture, then the
        newest samples will be kept. If Tcapture = 0 then new values are not appended
        to the data_capture array.

        """
        self.capture_sample_count = self.capture_sample_count + len(new_data_left) + len(new_data_right)
        if self.Tcapture > 0:
            self.data_capture_left = np.hstack((self.data_capture_left,new_data_left))
            self.data_capture_right = np.hstack((self.data_capture_right,new_data_right))
            if (len(self.data_capture_left) > self.Ncapture):
                self.data_capture_left = self.data_capture_left[-self.Ncapture:]
            if (len(self.data_capture_right) > self.Ncapture):
                self.data_capture_right = self.data_capture_right[-self.Ncapture:]

def DSP_capture_add_samples_stereo ( self , new_data_left , new_data_right ) : self . capture_sample_count = self . capture_sample_count + len ( new_data_left ) + len ( new_data_right ) if self . Tcapture > 0 : self . data_capture_left = np . hstack ( ( self . data_capture_left , new_data_left ) ) self . data_capture_right = np . hstack ( ( self . data_capture_right , new_data_right ) ) if ( len ( self . data_capture_left ) > self . Ncapture ) : self . data_capture_left = self . data_capture_left [ - self . Ncapture : ] if ( len ( self . data_capture_right ) > self . Ncapture ) : self . data_capture_right = self . data_capture_right [ - self . Ncapture : ]

Append new samples to the data_capture_left array and the data_capture_right array and increment the sample counter . If length reaches Tcapture then the newest samples will be kept . If Tcapture = 0 then new values are not appended to the data_capture array .


def DSP_callback_tic(self):
        """
        Add new tic time to the DSP_tic list. Will not be called if
        Tcapture = 0.

        """
        if self.Tcapture > 0:
            self.DSP_tic.append(time.time()-self.start_time)

def DSP_callback_tic ( self ) : if self . Tcapture > 0 : self . DSP_tic . append ( time . time ( ) - self . start_time )

Add new tic time to the DSP_tic list . Will not be called if Tcapture = 0 .


def DSP_callback_toc(self):
        """
        Add new toc time to the DSP_toc list. Will not be called if
        Tcapture = 0.

        """
        if self.Tcapture > 0:
            self.DSP_toc.append(time.time()-self.start_time)

def DSP_callback_toc ( self ) : if self . Tcapture > 0 : self . DSP_toc . append ( time . time ( ) - self . start_time )

Add new toc time to the DSP_toc list . Will not be called if Tcapture = 0 .


def IIR_bsf(f_pass1, f_stop1, f_stop2, f_pass2, Ripple_pass, Atten_stop,
            fs = 1.00, ftype = 'butter'):
    """
    Design an IIR bandstop filter using scipy.signal.iirdesign.
    The filter order is determined based on
    f_pass Hz, f_stop Hz, and the desired stopband attenuation
    d_stop in dB, all relative to a sampling rate of fs Hz.

    Mark Wickert October 2016
    """

    b,a = signal.iirdesign([2*float(f_pass1)/fs, 2*float(f_pass2)/fs],
                           [2*float(f_stop1)/fs, 2*float(f_stop2)/fs],
                           Ripple_pass, Atten_stop,
                           ftype = ftype, output='ba')
    sos = signal.iirdesign([2*float(f_pass1)/fs, 2*float(f_pass2)/fs],
                           [2*float(f_stop1)/fs, 2*float(f_stop2)/fs],
                           Ripple_pass, Atten_stop,
                           ftype =ftype, output='sos')
    tag = 'IIR ' + ftype + ' order'
    print('%s = %d.' % (tag,len(a)-1))
    return b, a, sos

def
IIR_bsf
(
f_pass1
,
f_stop1
,
f_stop2
,
f_pass2
,
Ripple_pass
,
Atten_stop
,
fs
=
1.00
,
ftype
=
'butter'
)
:
b
,
a
=
signal
.
iirdesign
(
[
2
*
float
(
f_pass1
)
/
fs
,
2
*
float
(
f_pass2
)
/
fs
]
,
[
2
*
float
(
f_stop1
)
/
fs
,
2
*
float
(
f_stop2
)
/
fs
]
,
Ripple_pass
,
Atten_stop
,
ftype
=
ftype
,
output
=
'ba'
)
sos
=
signal
.
iirdesign
(
[
2
*
float
(
f_pass1
)
/
fs
,
2
*
float
(
f_pass2
)
/
fs
]
,
[
2
*
float
(
f_stop1
)
/
fs
,
2
*
float
(
f_stop2
)
/
fs
]
,
Ripple_pass
,
Atten_stop
,
ftype
=
ftype
,
output
=
'sos'
)
tag
=
'IIR '
+
ftype
+
' order'
print
(
'%s = %d.'
%
(
tag
,
len
(
a
)
1
)
)
return
b
,
a
,
sos

Design an IIR bandstop filter using scipy . signal . iirdesign . The filter order is determined based on f_pass Hz f_stop Hz and the desired stopband attenuation d_stop in dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016


def freqz_cas(sos,w):
    """
    Cascade frequency response

    Mark Wickert October 2016
    """
    Ns,Mcol = sos.shape
    w,Hcas = signal.freqz(sos[0,:3],sos[0,3:],w)
    for k in range(1,Ns):
        w,Htemp = signal.freqz(sos[k,:3],sos[k,3:],w)
        Hcas *= Htemp
    return w, Hcas

def freqz_cas ( sos , w ) : Ns , Mcol = sos . shape w , Hcas = signal . freqz ( sos [ 0 , : 3 ] , sos [ 0 , 3 : ] , w ) for k in range ( 1 , Ns ) : w , Htemp = signal . freqz ( sos [ k , : 3 ] , sos [ k , 3 : ] , w ) Hcas *= Htemp return w , Hcas


def unique_cpx_roots(rlist,tol = 0.001):
    """

    The average of the root values is used when multiplicity
    is greater than one.

    Mark Wickert October 2016
    """
    uniq = [rlist[0]]
    mult = [1]
    for k in range(1,len(rlist)):
        N_uniq = len(uniq)
        for m in range(N_uniq):
            if abs(rlist[k]-uniq[m]) <= tol:
                mult[m] += 1
                uniq[m] = (uniq[m]*(mult[m]-1) + rlist[k])/float(mult[m])
                break
        uniq = np.hstack((uniq,rlist[k]))
        mult = np.hstack((mult,[1]))
    return np.array(uniq), np.array(mult)

def unique_cpx_roots ( rlist , tol = 0.001 ) : uniq = [ rlist [ 0 ] ] mult = [ 1 ] for k in range ( 1 , len ( rlist ) ) : N_uniq = len ( uniq ) for m in range ( N_uniq ) : if abs ( rlist [ k ] - uniq [ m ] ) <= tol : mult [ m ] += 1 uniq [ m ] = ( uniq [ m ] * ( mult [ m ] - 1 ) + rlist [ k ] ) / float ( mult [ m ] ) break uniq = np . hstack ( ( uniq , rlist [ k ] ) ) mult = np . hstack ( ( mult , [ 1 ] ) ) return np . array ( uniq ) , np . array ( mult )

The average of the root values is used when multiplicity is greater than one . Mark Wickert October 2016


def firwin_bpf(N_taps, f1, f2, fs = 1.0, pass_zero=False):
    """
    Design a windowed FIR bandpass filter in terms of passband
    critical frequencies f1 < f2 in Hz relative to sampling rate
    fs in Hz. The number of taps must be provided.

    Mark Wickert October 2016
    """
    return signal.firwin(N_taps,2*(f1,f2)/fs,pass_zero=pass_zero)

def firwin_bpf ( N_taps , f1 , f2 , fs = 1.0 , pass_zero = False ) : return signal . firwin ( N_taps , 2 * ( f1 , f2 ) / fs , pass_zero = pass_zero )

Design a windowed FIR bandpass filter in terms of passband critical frequencies f1 < f2 in Hz relative to sampling rate fs in Hz . The number of taps must be provided . Mark Wickert October 2016


def fir_remez_lpf(f_pass, f_stop, d_pass, d_stop, fs = 1.0, N_bump=5):
    """
    Design an FIR lowpass filter using remez with order
    determination. The filter order is determined based on
    f_pass Hz, fstop Hz, and the desired passband ripple
    d_pass dB and stopband attenuation d_stop dB all
    relative to a sampling rate of fs Hz.

    Mark Wickert October 2016, updated October 2018
    """
    n, ff, aa, wts = lowpass_order(f_pass, f_stop, d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts,Hz=2)
    print('Remez filter taps = %d.' % N_taps)
    return b

Design an FIR lowpass filter using remez with order determination . The filter order is determined based on f_pass Hz fstop Hz and the desired passband ripple d_pass dB and stopband attenuation d_stop dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016 updated October 2018


def fir_remez_hpf(f_stop, f_pass, d_pass, d_stop, fs = 1.0, N_bump=5):
    """
    Design an FIR highpass filter using remez with order
    determination. The filter order is determined based on
    f_pass Hz, fstop Hz, and the desired passband ripple
    d_pass dB and stopband attenuation d_stop dB all
    relative to a sampling rate of fs Hz.

    Mark Wickert October 2016, updated October 2018
    """
    # Transform HPF critical frequencies to lowpass equivalent
    f_pass_eq = fs/2. - f_pass
    f_stop_eq = fs/2. - f_stop
    # Design LPF equivalent
    n, ff, aa, wts = lowpass_order(f_pass_eq, f_stop_eq, d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts,Hz=2)
    # Transform LPF equivalent to HPF
    n = np.arange(len(b))
    b *= (-1)**n
    print('Remez filter taps = %d.' % N_taps)
    return b

Design an FIR highpass filter using remez with order determination . The filter order is determined based on f_pass Hz fstop Hz and the desired passband ripple d_pass dB and stopband attenuation d_stop dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016 updated October 2018


def fir_remez_bpf(f_stop1, f_pass1, f_pass2, f_stop2, d_pass, d_stop,
                  fs = 1.0, N_bump=5):
    """
    Design an FIR bandpass filter using remez with order
    determination. The filter order is determined based on
    f_stop1 Hz, f_pass1 Hz, f_pass2 Hz, f_stop2 Hz, and the
    desired passband ripple d_pass dB and stopband attenuation
    d_stop dB all relative to a sampling rate of fs Hz.

    Mark Wickert October 2016, updated October 2018
    """
    n, ff, aa, wts = bandpass_order(f_stop1, f_pass1, f_pass2, f_stop2,
                                  d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts,Hz=2)
    print('Remez filter taps = %d.' % N_taps)
    return b

Design an FIR bandpass filter using remez with order determination . The filter order is determined based on f_stop1 Hz f_pass1 Hz f_pass2 Hz f_stop2 Hz and the desired passband ripple d_pass dB and stopband attenuation d_stop dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016 updated October 2018


def fir_remez_bsf(f_pass1, f_stop1, f_stop2, f_pass2, d_pass, d_stop,
                  fs = 1.0, N_bump=5):
    """
    Design an FIR bandstop filter using remez with order
    determination. The filter order is determined based on
    f_pass1 Hz, f_stop1 Hz, f_stop2 Hz, f_pass2 Hz, and the
    desired passband ripple d_pass dB and stopband attenuation
    d_stop dB all relative to a sampling rate of fs Hz.

    Mark Wickert October 2016, updated October 2018
    """
    n, ff, aa, wts = bandstop_order(f_pass1, f_stop1, f_stop2, f_pass2,
                                    d_pass, d_stop, fsamp=fs)
    # Bump up the order by N_bump to bring down the final d_pass & d_stop
    # Initially make sure the number of taps is even so N_bump needs to be odd
    if np.mod(n,2) != 0:
        n += 1
    N_taps = n
    N_taps += N_bump
    b = signal.remez(N_taps, ff, aa[0::2], wts, Hz=2,
                     maxiter = 25, grid_density = 16)
    print('N_bump must be odd to maintain odd filter length')
    print('Remez filter taps = %d.' % N_taps)
    return b

Design an FIR bandstop filter using remez with order determination . The filter order is determined based on f_pass1 Hz f_stop1 Hz f_stop2 Hz f_pass2 Hz and the desired passband ripple d_pass dB and stopband attenuation d_stop dB all relative to a sampling rate of fs Hz . Mark Wickert October 2016 updated October 2018


def position_CD(Ka,out_type = 'fb_exact'):
    """
    CD sled position control case study of Chapter 18.

    The function returns the closed-loop and open-loop
    system function for a CD/DVD sled position control
    system. The loop amplifier gain is the only variable
    that may be changed. The returned system function can
    however be changed.

    Parameters
    ----------
    Ka : loop amplifier gain, start with 50.
    out_type : 'open_loop' for open loop system function
    out_type : 'fb_approx' for closed-loop approximation
    out_type : 'fb_exact' for closed-loop exact

    Returns
    -------
    b : numerator coefficient ndarray
    a : denominator coefficient ndarray

    Notes
    -----
    With the exception of the loop amplifier gain, all
    other parameters are hard-coded from Case Study example.

    Examples
    --------
    >>> b,a = position_CD(Ka,'fb_approx')
    >>> b,a = position_CD(Ka,'fb_exact')
    """
    rs = 10/(2*np.pi)
    # Load b and a ndarrays with the coefficients
    if out_type.lower() == 'open_loop':
        b = np.array([Ka*4000*rs])
        a = np.array([1,1275,31250,0])
    elif out_type.lower() == 'fb_approx':
        b = np.array([3.2*Ka*rs])
        a = np.array([1, 25, 3.2*Ka*rs])
    elif out_type.lower() == 'fb_exact':
        b = np.array([4000*Ka*rs])
        a = np.array([1, 1250+25, 25*1250, 4000*Ka*rs])
    else:
        raise ValueError('out_type must be: open_loop, fb_approx, or fc_exact')
    return b, a

CD sled position control case study of Chapter 18 .


def cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='H'):
    """
    Cruise control with PI controller and hill disturbance.

    This function returns various system function configurations
    for a the cruise control Case Study example found in
    the supplementary article. The plant model is obtained by the
    linearizing the equations of motion and the controller contains a
    proportional and integral gain term set via the closed-loop parameters
    natuarl frequency wn (rad/s) and damping zeta.

    Parameters
    ----------
    wn : closed-loop natural frequency in rad/s, nominally 0.1
    zeta : closed-loop damping factor, nominally 1.0
    T : vehicle time constant, nominally 10 s
    vcruise : cruise velocity set point, nominally 75 mph
    vmax : maximum vehicle velocity, nominally 120 mph
    tf_mode : 'H', 'HE', 'HVW', or 'HED' controls the system function returned by the function
    'H'   : closed-loop system function V(s)/R(s)
    'HE'  : closed-loop system function E(s)/R(s)
    'HVW' : closed-loop system function V(s)/W(s)
    'HED' : closed-loop system function E(s)/D(s), where D is the hill disturbance input

    Returns
    -------
    b : numerator coefficient ndarray
    a : denominator coefficient ndarray

    Examples
    --------
    >>> # return the closed-loop system function output/input velocity
    >>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='H')
    >>> # return the closed-loop system function loop error/hill disturbance
    >>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='HED')
    """
    tau = T/2.*vmax/vcruise
    g = 9.8
    g *= 3*60**2/5280. # m/s to mph conversion
    Kp = T*(2*zeta*wn-1/tau)/vmax
    Ki = T*wn**2./vmax
    K = Kp*vmax/T
    print('wn = ', np.sqrt(K/(Kp/Ki)))
    print('zeta = ', (K + 1/tau)/(2*wn))
    a = np.array([1, 2*zeta*wn, wn**2])
    if tf_mode == 'H':
        b = np.array([K, wn**2])
    elif tf_mode == 'HE':
        b = np.array([1, 2*zeta*wn-K, 0.])
    elif tf_mode == 'HVW':
        b = np.array([ 1, wn**2/K+1/tau, wn**2/(K*tau)])
        b *= Kp
    elif tf_mode == 'HED':
        b = np.array([g, 0])
    else:
        raise ValueError('tf_mode must be: H, HE, HVU, or HED')
    return b, a

def
cruise_control
(
wn
,
zeta
,
T
,
vcruise
,
vmax
,
tf_mode
=
'H'
)
:
tau
=
T
/
2.
*
vmax
/
vcruise
g
=
9.8
g
*=
3
*
60
**
2
/
5280.
# m/s to mph conversion
Kp
=
T
*
(
2
*
zeta
*
wn
1
/
tau
)
/
vmax
Ki
=
T
*
wn
**
2.
/
vmax
K
=
Kp
*
vmax
/
T
print
(
'wn = '
,
np
.
sqrt
(
K
/
(
Kp
/
Ki
)
)
)
print
(
'zeta = '
,
(
K
+
1
/
tau
)
/
(
2
*
wn
)
)
a
=
np
.
array
(
[
1
,
2
*
zeta
*
wn
,
wn
**
2
]
)
if
tf_mode
==
'H'
:
b
=
np
.
array
(
[
K
,
wn
**
2
]
)
elif
tf_mode
==
'HE'
:
b
=
np
.
array
(
[
1
,
2
*
zeta
*
wn
K
,
0.
]
)
elif
tf_mode
==
'HVW'
:
b
=
np
.
array
(
[
1
,
wn
**
2
/
K
+
1
/
tau
,
wn
**
2
/
(
K
*
tau
)
]
)
b
*=
Kp
elif
tf_mode
==
'HED'
:
b
=
np
.
array
(
[
g
,
0
]
)
else
:
raise
ValueError
(
'tf_mode must be: H, HE, HVU, or HED'
)
return
b
,
a

Cruise control with PI controller and hill disturbance .


def stereo_FM(x,fs=2.4e6,file_name='test.wav'):
    """
    Stereo demod from complex baseband at sampling rate fs.
    Assume fs is 2400 ksps

    Mark Wickert July 2017
    """
    N1 = 10
    b = signal.firwin(64,2*200e3/float(fs))
    # Filter and decimate (should be polyphase)
    y = signal.lfilter(b,1,x)
    z = ss.downsample(y,N1)
    # Apply complex baseband discriminator
    z_bb = discrim(z)
    # Work with the (3) stereo multiplex signals:
    # Begin by designing a lowpass filter for L+R and DSP demoded (L-R)
    # (fc = 12 KHz)
    b12 = signal.firwin(128,2*12e3/(float(fs)/N1))
    # The L + R term is at baseband, we just lowpass filter to remove
    # other terms above 12 kHz.
    y_lpr = signal.lfilter(b12,1,z_bb)
    b19 = signal.firwin(128,2*1e3*np.array([19-5,19+5])/(float(fs)/N1),
                        pass_zero=False);
    z_bb19 = signal.lfilter(b19,1,z_bb)
    # Lock PLL to 19 kHz pilot
    # A type 2 loop with bandwidth Bn = 10 Hz and damping zeta = 0.707
    # The VCO quiescent frequency is set to 19000 Hz.
    theta, phi_error = pilot_PLL(z_bb19,19000,fs/N1,2,10,0.707)
    # Coherently demodulate the L - R subcarrier at 38 kHz.
    # theta is the PLL output phase at 19 kHz, so to double multiply
    # by 2 and wrap with cos() or sin().
    # First bandpass filter
    b38 = signal.firwin(128,2*1e3*np.array([38-5,38+5])/(float(fs)/N1),
                        pass_zero=False);
    x_lmr = signal.lfilter(b38,1,z_bb)
    # Coherently demodulate using the PLL output phase
    x_lmr = 2*np.sqrt(2)*np.cos(2*theta)*x_lmr
    # Lowpass at 12 kHz to recover the desired DSB demod term
    y_lmr = signal.lfilter(b12,1,x_lmr)
    # Matrix the y_lmr and y_lpr for form right and left channels:
    y_left = y_lpr + y_lmr
    y_right = y_lpr - y_lmr

    # Decimate by N2 (nominally 5)
    N2 = 5
    fs2 = float(fs)/(N1*N2) # (nominally 48 ksps)
    y_left_DN2 = ss.downsample(y_left,N2)
    y_right_DN2 = ss.downsample(y_right,N2)
    # Deemphasize with 75 us time constant to 'undo' the preemphasis
    # applied at the transmitter in broadcast FM.
    # A 1-pole digital lowpass works well here.
    a_de = np.exp(-2.1*1e3*2*np.pi/fs2)
    z_left = signal.lfilter([1-a_de],[1, -a_de],y_left_DN2)
    z_right = signal.lfilter([1-a_de],[1, -a_de],y_right_DN2)
    # Place left and righ channels as side-by-side columns in a 2D array
    z_out = np.hstack((np.array([z_left]).T,(np.array([z_right]).T)))

    ss.to_wav(file_name, 48000, z_out/2)
    print('Done!')
    #return z_bb, z_out
    return z_bb, theta, y_lpr, y_lmr, z_out

Stereo demod from complex baseband at sampling rate fs . Assume fs is 2400 ksps Mark Wickert July 2017