Пример #1
0
    def __init__(self,x,y,xUnit,yUnit,params={}):
        '''
        Inputs:
                x = wavelength/frequency values, 1d array-like
                y = spectral flux density, 1d array-like
                xUnit = astropy.units.Unit of physical type length or frequency
                yUnit = astropy.units.Unit of physical type luminosity/flux density /length or frequency

        '''
        self.type = 'spectrum'
        # Validate x input
        wavelengths = np.asarray(x)
        spec = np.asarray(y)
        if wavelengths.ndim!=1:
            raise ValueError('Wavelength/Frequency must be given as a 1d array')
        if wavelengths.shape!=spec.shape:
            raise ValueError('Wavelength/Frequency array and flux density array have different shapes.')
        if u.get_physical_type(xUnit) not in ['frequency','length']:
            raise TypeError(xUnit,' is neither a unit of frequency nor a unit of length. Get it together.')
        wavelengths = wavelengths * xUnit
        wavelengths.to('micron',equivalencies=u.spectral())
        if not np.all(np.ediff1d(wavelengths)>0.):
            if u.get_physical_type(xUnit) == 'frequency':
                raise ValueError('Frequencies must be monotonically decreasing.')
            else:
                raise ValueError('Wavelengths must be monotonically increasing.')
        self.wavelengths = wavelengths
        
        # Validate y input
        spec = np.asarray(y)
        if spec.ndim!=1:
            raise ValueError('Spectrum must be given as a 1d array')
            
        # Define desired flux units from base units
        uFnu = u.Unit('erg')/u.Unit('s')/u.Unit('Hz') 
        uFnuN = u.Unit('erg')/u.Unit('s')/u.Unit('Hz')/u.Unit('cm')**2 # astropy equivalency only works when area uncluded
        uFlam = u.Unit('erg')/u.Unit('s')/u.Unit('Angstrom')
        uFlamN = u.Unit('erg')/u.Unit('s')/u.Unit('Angstrom')/u.Unit('cm')**2
        
        # Need F_nu, but if there's no area, need to add because too lazy to add equivalency 
        if yUnit.is_equivalent(uFnu):
            spec = spec * yUnit / (4*np.pi*((10 * u.Unit('parsec')).to('cm'))**2)
            #spec = spec * yUnit / u.Unit('m')**2
        elif yUnit.is_equivalent(uFlam):
            spec = spec * yUnit/ (4*np.pi*((10 * u.Unit('parsec')).to('cm'))**2)
            #spec = spec * yUnit/ u.Unit('m')**2
        else:
            spec = (spec * yUnit) . to(uFnuN)
        
        if not spec.unit.is_equivalent(uFlamN) and not spec.unit.is_equivalent(uFnuN):
            raise ValueError(spec.unit,' not recognized as a unit of spectral flux density.')
        spec = spec.to(uFnuN,equivalencies=u.spectral_density(wavelengths))
        
        if wavelengths[-1]<wavelengths[0]: # then we originally had flux units
            wavelengths = np.flipud(wavelengths)
            spec = np.flipud(spec)
        self.spec = spec
        self.params = params
Пример #2
0
def _validate_unit_type(name, value, expected_unit):
    _validate_type(name, value, Quantity)
    if isinstance(expected_unit, Quantity):
        expected_unit = expected_unit.unit
    if au.get_physical_type(value.unit) != au.get_physical_type(expected_unit):
        raise ValueError("{name} units should be {expected_unit}, got {type}.".format(name=name,
                                                                                      expected_unit=au.get_physical_type(
                                                                                          expected_unit),
                                                                                      type=au.get_physical_type(
                                                                                          value.unit)))
Пример #3
0
def calc_model_lc(spectra, transient, filter):
    spectra_ = copy.deepcopy(spectra)
    spectra_ = spectra_.redshift(z=transient.redshift)
    spectra_ = spectra_.dust_extinction(Eb_v = transient.Eb_v, model="maeda")
    lc = photontools.calc_band_flux(spectra_, filter)
    if (u.get_physical_type((transient.luminosity_distance * u.m / u.m).unit) == "length"):
        lc = lc.convert_flux_to_magnitude(filter, system="AB", distance=transient.luminosity_distance)
    elif (u.get_physical_type((transient.luminosity_distance * u.m / u.m).unit) == "dimensionless"):
        lc = lc.convert_flux_to_magnitude(filter, system="AB", distance=transient.luminosity_distance * u.Mpc)
    else:
        raise ValueError("Input luminosity_distnace unit is wrong!")
    return lc
Пример #4
0
 def __init__(self,x,xUnit,y,filtName):
     '''
     x = values of frequency or wavelength at which FTC is defined, array-like
     xUnit = astropy.units.Unit of type lengthor frequency
     y = filter transmission at x, array of length (len(x))
     '''
     self.type='filter'
     self.name=filtName
     wavelengths = np.asarray(x)
     ftc = np.asarray(y)
     if x.ndim!=1:
         raise ValueError('Wavelength/Frequency must be given as a 1d array.')
     if wavelengths.shape!=ftc.shape:
         raise ValueError('Wavelength/Frequency array and transmission array have different shapes.')
     if u.get_physical_type(xUnit) not in ['frequency','length']:
         raise TypeError(xUnit,' is neither a unit of frequency nor a unit of length. Get it together.')
     
     # Convert wavelengths/frequencies to microns
     wavelengths = x * xUnit
     wavelengths = wavelengths.to('Angstrom',equivalencies=u.spectral())
     
     # Eliminate any negative transmission values
     ftc = y
     ftc[y<0.]=0.
     # Reverse array orders if FTC was defined in frequency space
     if wavelengths[-1]<wavelengths[0]:
         wavelengths = np.flipud(wavelengths)
         ftc = np.flipud(ftc)
     self.wavelengths = wavelengths
     self.ftc = ftc
Пример #5
0
 def __init__(self,ftcFile,unit,filtName):
     # Validate unit 
     try:
         unitType = u.get_physical_type(u.Unit(unit))
     except:
         os.system("say '"+unit+" is not a recognized unit. Don't waste my time "+name+"'")
         raise TypeError(unit," is not a recognized unit. Don't waste my time.")
     if unitType not in ['length','frequency']:
         os.system("say '"+unit+" is neither a unit of frequency nor a unit of length. Get it together "+str(name)+"'")
         raise TypeError(unit,' is neither a unit of frequency nor a unit of length. Get it together.')
     self.name = filtName    
     # Load FTC
     if unitType == 'length':
         self.ftcLambda, self.ftcTransLam = np.loadtxt(ftcFile,unpack=True)
         self.ftcLambda = self.ftcLambda * u.Unit(unit)
         self.ftcLambda = self.ftcLambda.to(u.micron)
         self.ftcNu = const.c / self.ftcLambda
         self.ftcNu = self.ftcNu.to(u.Hz)
         self.ftcNu = self.ftcNu[::-1]
         self.ftcTransLam[self.ftcTransLam<0.] = 0.
         self.ftcTransNu = self.ftcTransLam[::-1]
         
     if unitType == 'frequency':
         self.ftcNu, self.ftcTransNu = np.loadtxt(ftcFile,unpack=True)
         self.ftcNu = self.ftcNu * u.Unit(unit)
         self.ftcNu = self.ftcNu.to(u.Hz)
         self.ftcLambda = const.c / self.ftcNu
         self.ftcLambda = self.ftcLambda.to(u.AA)
         self.ftcTransNu[self.ftcTransNu<0.] = 0.
         self.ftcTransLam = self.ftcTransNu[::-1]
Пример #6
0
def indices_xfind_coords(
    coords1,
    coords2,
    maxdist=1 * u.arcsec,
    obstime=None,
):
    """Indices of X-Find coords.

    Returns
    -------
    idx1 : integer array
    idx2 : integer array
        indices into `other` for all coordinates which have a "match"
        in `catalog`.
    info : dict
        Useful information  # TODO

    See Also
    --------
    :func:`~astropy.coordinates.search_around_sky`
    :func:`~astropy.coordinates.search_around_3d`

    """
    if u.get_physical_type(maxdist.unit) == "angle":
        idx1, idx2, sep2d, dist3d = coord.search_around_sky(
            coords1,
            coords2,
            seplimit=maxdist,
        )
    elif u.get_physical_type(maxdist.unit) == "length":
        idx1, idx2, sep2d, dist3d = coord.search_around_3d(
            coords1,
            coords2,
            distlimit=maxdist,
        )

    info = {"sep2d": sep2d, "dist3d": dist3d}

    return idx1, idx2, info
Пример #7
0
    def __init__(self, ftcFile, unit, filtName):
        # Validate unit
        try:
            unitType = u.get_physical_type(u.Unit(unit))
        except:
            os.system("say '" + unit +
                      " is not a recognized unit. Don't waste my time " +
                      name + "'")
            raise TypeError(unit,
                            " is not a recognized unit. Don't waste my time.")
        if unitType not in ['length', 'frequency']:
            os.system(
                "say '" + unit +
                " is neither a unit of frequency nor a unit of length. Get it together "
                + str(name) + "'")
            raise TypeError(
                unit,
                ' is neither a unit of frequency nor a unit of length. Get it together.'
            )
        self.name = filtName
        # Load FTC
        if unitType == 'length':
            self.ftcLambda, self.ftcTransLam = np.loadtxt(ftcFile, unpack=True)
            self.ftcLambda = self.ftcLambda * u.Unit(unit)
            self.ftcLambda = self.ftcLambda.to(u.micron)
            self.ftcNu = const.c / self.ftcLambda
            self.ftcNu = self.ftcNu.to(u.Hz)
            self.ftcNu = self.ftcNu[::-1]
            self.ftcTransLam[self.ftcTransLam < 0.] = 0.
            self.ftcTransNu = self.ftcTransLam[::-1]

        if unitType == 'frequency':
            self.ftcNu, self.ftcTransNu = np.loadtxt(ftcFile, unpack=True)
            self.ftcNu = self.ftcNu * u.Unit(unit)
            self.ftcNu = self.ftcNu.to(u.Hz)
            self.ftcLambda = const.c / self.ftcNu
            self.ftcLambda = self.ftcLambda.to(u.AA)
            self.ftcTransNu[self.ftcTransNu < 0.] = 0.
            self.ftcTransLam = self.ftcTransNu[::-1]
Пример #8
0
    def __init__(self, x, xUnit, y, filtName):
        '''
        x = values of frequency or wavelength at which FTC is defined, array-like
        xUnit = astropy.units.Unit of type lengthor frequency
        y = filter transmission at x, array of length (len(x))
        '''
        self.type = 'filter'
        self.name = filtName
        wavelengths = np.asarray(x)
        ftc = np.asarray(y)
        if x.ndim != 1:
            raise ValueError(
                'Wavelength/Frequency must be given as a 1d array.')
        if wavelengths.shape != ftc.shape:
            raise ValueError(
                'Wavelength/Frequency array and transmission array have different shapes.'
            )
        if u.get_physical_type(xUnit) not in ['frequency', 'length']:
            raise TypeError(
                xUnit,
                ' is neither a unit of frequency nor a unit of length. Get it together.'
            )

        # Convert wavelengths/frequencies to microns
        wavelengths = x * xUnit
        wavelengths = wavelengths.to('Angstrom', equivalencies=u.spectral())

        # Eliminate any negative transmission values
        ftc = y
        ftc[y < 0.] = 0.
        # Reverse array orders if FTC was defined in frequency space
        if wavelengths[-1] < wavelengths[0]:
            wavelengths = np.flipud(wavelengths)
            ftc = np.flipud(ftc)
        self.wavelengths = wavelengths
        self.ftc = ftc
Пример #9
0
def _get_physical_type_dict(
    iterable: Iterable,
    *,
    only_quantities=False,
    numbers_become_quantities=False,
) -> Dict[u.PhysicalType, u.Quantity]:
    """
    Return a `dict` that contains `~astropy.units.PhysicalType` objects
    as keys and the corresponding objects in ``iterable`` as values.

    Objects in ``iterable`` that do not correspond to a |PhysicalType|
    are skipped.

    Parameters
    ----------
    iterable : iterable
        A iterable that is expected to contain objects with physical
        types.

    only_quantities : `bool`, keyword-only, optional
        If `True`, only `~astropy.units.Quantity` instances in
        ``iterable`` will be passed into the resulting `dict`. If
        `False`, then any unit, |PhysicalType|, or object that can be
        converted to a |Quantity| or that has a physical type will be
        included in the `dict`. Defaults to `False`.

    numbers_become_quantities : `bool`, keyword-only, optional
        If `True`, `~numbers.Number` objects will be converted into
        dimensionless |Quantity| instances. If `False`,
        `~numbers.Number` objects will be skipped. Defaults to `False`.

    Returns
    -------
    physical_types : `dict`
        A mapping from |PhysicalType| instances to the corresponding
        objects in ``iterable``.

    Examples
    --------
    >>> import astropy.units as u
    >>> from plasmapy.utils.units_helpers import _get_physical_type_dict
    >>> quantities = [1 * u.m, 2 * u.kg]
    >>> _get_physical_type_dict(quantities)
    {PhysicalType('length'): <Quantity 1. m>, PhysicalType('mass'): <Quantity 2. kg>}
    """
    physical_types = {}

    for obj in iterable:

        if isinstance(obj, Number) and numbers_become_quantities:
            obj = u.Quantity(obj, u.dimensionless_unscaled)

        if only_quantities and not isinstance(obj, u.Quantity):
            continue

        try:
            physical_type = u.get_physical_type(obj)
        except (TypeError, ValueError):
            pass
        else:
            if physical_type in physical_types:
                raise ValueError(f"Duplicate physical type: {physical_type}")
            physical_types[physical_type] = obj

    return physical_types
Пример #10
0
def create_model(line_centers, amp_guess=None,
                 center_guess=None, width_guess=None,
                 center_limits=None, width_limits=None,
                 center_fixed=None, width_fixed=None,
                 lambda_units=u.micron):
    """
    Function that allows for the creation of a generic model for a spectral region.
    Each line specified in 'line_names' must be included in the file 'lines.py'.
    Defaults for the amplitude guesses will be 1.0 for all lines.
    Defaults for the center guesses will be the observed wavelengths.
    Defaults for the line widths will be 100 km/s for narrow lines and 1000 km/s for the
    broad lines.
    All lines are considered narrow unless the name has 'broad' attached to the end of the name.
    """

    nlines = len(line_centers.keys())
    line_names = line_centers.keys()

    # Create the default amplitude guesses for the lines if necessary
    if amp_guess is None:
        amp_guess = {l: 1.0 for l in line_names}

    # Create arrays to hold the default line center and width guesses
    if center_guess is None:
        center_guess = {l: 0*u.km/u.s for l in line_names}
    if width_guess is None:
        width_guess = {l: 100.*u.km/u.s for l in line_names}

    # Loop through each line and create a model
    mods = []
    for i, l in enumerate(line_names):

        # Equivalency to convert to/from wavelength from/to velocity
        opt_conv = u.doppler_optical(line_centers[l])

        # Convert the guesses for the line center and width to micron
        center_guess_i = center_guess[l].to(lambda_units, equivalencies=opt_conv)

        if u.get_physical_type(width_guess[l].unit) == 'speed':

            width_guess_i = (width_guess[l].to(lambda_units,
                                               equivalencies=u.doppler_optical(center_guess_i)) -
                             center_guess_i)

        elif u.get_physical_type(width_guess[l].unit) == 'length':

            width_guess_i = width_guess[i].to(lambda_units)

        center_guess_i = center_guess_i.value
        width_guess_i = width_guess_i.value

        # Create the single Gaussian line model for the emission line
        mod_single = apy_mod.models.Gaussian1D(mean=center_guess_i, amplitude=amp_guess[l],
                                               stddev=width_guess_i, name=l)

        # Set the constraints on the parameters if necessary
        mod_single.amplitude.min = 0      # always an emission line

        if center_limits is not None:
            if center_limits[l][0] is not None:
                mod_single.mean.min = center_limits[l][0].to(lambda_units, equivalencies=opt_conv).value
            if center_limits[l][1] is not None:
                mod_single.mean.max = center_limits[l][1].to(lambda_units, equivalencies=opt_conv).value

        if width_limits is not None:
            if width_limits[l][0] is not None:
                mod_single.stddev.min = width_limits[l][0].to(lambda_units, equivalencies=opt_conv).value - line_centers[l].value
            else:
                mod_single.stddev.min = 0         # can't have negative width
            if width_limits[l][1] is not None:
                mod_single.stddev.max = width_limits[l][1].to(lambda_units, equivalencies=opt_conv).value - line_centers[l].value
        else:
            mod_single.stddev.min = 0

        # Set the fixed parameters
        if center_fixed is not None:
            mod_single.mean.fixed = center_fixed[l]
        if width_fixed is not None:
            mod_single.stddev.fixed = width_fixed[l]

        # Add to the model list
        mods.append(mod_single)

    # Create the combined model by adding all of the models together
    if nlines == 1:
        final_model = mods[0]
    else:
        final_model = mods[0]
        for m in mods[1:]:
            final_model += m

    return final_model
Пример #11
0
def indices_xmatch_coords(
    catalog,
    other,
    maxdist=1 * u.arcsec,
    obstime=None,
    nthneighbor: int = 1,
):
    """Basic 2-catalog x-match. Returns match indices.

    see https://docs.astropy.org/en/stable/coordinates/matchsep.html

    Parameters
    ----------
    catalog, other : SkyCoord or BaseCoordinateFrame
        `catalog` is the "catalogcoord", `other` are the "matchcoord".
        Note that this is in the opposite order as Astropy.
    maxdist : `~astropy.units.Angle` or `~astropy.coordinates.Distance`, optional
        The maximum separation to be considered a match.
        If Angle, does an on-sky x-match using
        :func:`~astropy.coordinates.match_coordinates_sky`.
        If Distance, does a 3d x-match using
        :func:`~astropy.coordinates.match_coordinates_3d`.
    obstime : Time, optional
        If provided, the "epoch" at which to x-match the coords.
        If None (default), will use the obstime of the first catalog to have
        an obstime. An absence of obstime in a catalog means the coordinates
        are *right now*, if this is not the case, ensure the catalog has this
        information.

    Returns
    -------
    catalog_idx : integer array
        indices into `catalog` for the x-match.
    info : dict
        Useful information.

            - sep2d : on-sky separation (Angle)
            - dist3d : 3D distance (Quantity)

    Other Parameters
    ----------------
    nthneighbor : int
        The nthneighbor to use in ``match_coordinates_``.
        TODO, rename to "_nth" but "quantity_input" can't handle underscores.

    See Also
    --------
    :func:`~astropy.coordinates.match_coordinates_sky`
    :func:`~astropy.coordinates.match_coordinates_3d`

    """
    if u.get_physical_type(maxdist.unit) == "angle":
        idx, sep2d, dist3d = coord.match_coordinates_sky(
            matchcoord=other,
            catalogcoord=catalog,
            nthneighbor=nthneighbor,
        )
        sep = sep2d  # separation constraints on this
    elif u.get_physical_type(maxdist.unit) == "length":
        idx, sep2d, dist3d = coord.match_coordinates_3d(
            matchcoord=other,
            catalogcoord=catalog,
            nthneighbor=nthneighbor,
        )
        sep = dist3d  # separation constraints on this

    # separation constraints
    midx = np.where(sep < maxdist)[0]

    if len(midx) == 0:  # no matches
        return False, False, {}
    elif len(midx) == 1:  # correct for case of only 1 match
        midx = midx[0]
        sel = ...
    else:
        sel = midx

    catalog_idx = idx[sel]
    other_idx = midx

    info = {"sep2d": sep2d[sel], "dist3d": dist3d[sel]}

    return catalog_idx, other_idx, info
Пример #12
0
un.add_enabled_units([littleh])


class UnitError(ValueError):
    """An error pertaining to having incorrect units."""

    pass


Length = un.Quantity["length"]
Meters = un.Quantity["m"]
Time = un.Quantity["time"]
Frequency = un.Quantity["frequency"]
Temperature = un.Quantity["temperature"]
TempSquared = un.Quantity[un.get_physical_type("temperature")**2]
Wavenumber = un.Quantity[littleh / un.Mpc]
Delta = un.Quantity[un.mK**2]

time_as_distance = [(
    un.s,
    un.m,
    lambda x: cnst.c.to_value("m/s") * x,
    lambda x: x / cnst.c.to_value("m/s"),
)]


def vld_physical_type(unit: str) -> Callable[[Any, attr.Attribute, Any], None]:
    """Attr validator to check physical type."""
    def _check_type(self: Any, att: attr.Attribute, val: Any):
        if not isinstance(val, un.Quantity):
Пример #13
0
from plasmapy.utils.units_helpers import _get_physical_type_dict


def test_get_physical_type_dict_specific_example():
    units = [u.m, u.m**-3, u.m * u.s]
    quantities = [5 * unit for unit in units]
    expected = {
        quantity.unit.physical_type: quantity
        for quantity in quantities
    }
    new_physical_type_dict = _get_physical_type_dict(quantities)
    assert new_physical_type_dict == expected


velocity = u.get_physical_type("velocity")
mass = u.get_physical_type("mass")
dimensionless = u.get_physical_type(1)

test_case = namedtuple("case", ["collection", "kwargs", "expected"])

test_cases = [
    test_case(
        collection=(c, m_e),
        kwargs={},
        expected={
            velocity: c,
            mass: m_e
        },
    ),
    test_case(
Пример #14
0
def check_param_values(model, **user_param):
    """Performs generic check that the requested model parameters have valid values and units for the requested
    SNEWPY model.
    Parameters
    ----------
    model : snewpy.model.SupernovaModel
        Model class used to perform parameter check
    user_param : varies
        User-requested model parameters to be tested for validity. MUST be provided as keyword arguments that match the
        model `param` class member
    Raises
    ------
    ValueError
        If invalid model parameters are provided based on units, allowed values, etc.
    UnitTypeError
        If invalid units are provided for a model parameter
    See Also
    --------
    snewpy.models.ccsn
    snewpy.models.presn
    """
    model_param = model.param
    # Check that the appropriate number of params are provided
    if len(user_param) != len(model_param):
        raise ValueError(
            f"Invalid model parameters, expected {len(model_param)} "
            f"but {len(user_param)} were given")

    # Check that user-requested params have valid units and values
    for (key, allowed_params), user_param in zip(model_param.items(),
                                                 user_param.values()):
        # If both have units, check that the user param value is valid. If valid, continue. Else, error
        if type(user_param) == Quantity and type(allowed_params) == Quantity:
            if get_physical_type(user_param.unit) != get_physical_type(
                    allowed_params.unit):
                raise UnitTypeError(
                    f"Incorrect units {user_param.unit} provided for parameter {key}, "
                    f"expected {allowed_params.unit}")
            elif user_param.to(
                    allowed_params.unit).value in allowed_params.value:
                continue
            else:
                raise ValueError(
                    f"Invalid value '{user_param}' provided for parameter {key}, "
                    f"allowed value(s): {allowed_params}")

        # If one only one has units, then error
        elif (type(user_param) == Quantity) ^ (type(allowed_params)
                                               == Quantity):
            # User param has units, model param is unitless
            if type(user_param) == Quantity:
                raise ValueError(
                    f"Invalid units {user_param.unit} for parameter {key} provided, expected None"
                )
            else:
                raise ValueError(
                    f"Missing units for parameter {key}, expected {allowed_params.unit}"
                )

        # Check that unitless user param value is valid. If valid, continue. Else, Error
        elif user_param in allowed_params:
            continue
        else:
            raise ValueError(
                f"Invalid value '{user_param}' provided for parameter {key}, "
                f"allowed value(s): {allowed_params}")
Пример #15
0
    def grid_baselines(
        self,
        baselines: tp.Length | None = None,
        weights: np.ndarray | None = None,
        integration_time: tp.Time = 60.0 * un.s,
        bl_min: tp.Length = 0 * un.m,
        bl_max: tp.Length = np.inf * un.m,
        observation_duration: tp.Time | None = None,
        ndecimals: int = 1,
    ) -> np.ndarray:
        """
        Grid baselines onto a pre-determined uvgrid, accounting for earth rotation.

        Parameters
        ----------
        baselines : array_like, optional
            The baseline co-ordinates to project, assumed to be in metres.
            If not provided, calculates effective baselines by finding redundancies on
            all baselines in the observatory. Shape of the array can be (N,N,3) or (N, 3).
            The co-ordinates are expected to be in ENU. If `baselines` is provided,
            `weights` must also be provided.
        weights: array_like, optional
            An array of the same length as `baselines`, giving the number of independent
            baselines at each co-ordinate. If not provided, calculates effective
            baselines by finding redundancies on all baselines in the observatory.
            If `baselines` is provided, `weights` must also be provided.
        integration_time : float or Quantity, optional
            The amount of time integrated into a snapshot visibility, assumed
            to be in seconds.
        bl_min : float or Quantity, optional
            Minimum baseline length (in meters) to include in the gridding.
        bl_max : float or Quantity, optional
            Maximum baseline length (in meters) to include in the gridding.
        observation_duration : float or Quantity, optional
            Amount of time in a single (coherent) LST bin, assumed to be in minutes.
        ndecimals : int, optional
            Number of decimals to which baselines must match to be considered redundant.

        Returns
        -------
        array :
            Shape [n_baseline_groups, Nuv, Nuv]. The coherent sum of baselines within
            grid cells given by :attr:`ugrid`. One can treat different baseline groups
            independently, or sum over them.

        See Also
        --------
        grid_baselines_coherent :
            Coherent sum over baseline groups of the output of this method.
        grid_basleine_incoherent :
            Incoherent sum over baseline groups of the output of this method.
        """
        if baselines is not None:
            assert un.get_physical_type(baselines) == "length"
            assert baselines.ndim in (2, 3)

        assert un.get_physical_type(integration_time) == "time"
        assert un.get_physical_type(bl_min) == "length"
        assert un.get_physical_type(bl_max) == "length"
        if observation_duration is not None:
            assert un.get_physical_type(observation_duration) == "time"

        if baselines is None:
            baseline_groups = self.get_redundant_baselines(bl_min=bl_min,
                                                           bl_max=bl_max,
                                                           ndecimals=ndecimals)
            baselines = self.baseline_coords_from_groups(baseline_groups)
            weights = self.baseline_weights_from_groups(baseline_groups)

        if weights is None:
            raise ValueError(
                "If baselines are provided, weights must also be provided.")

        time_offsets = self.time_offsets_from_obs_int_time(
            integration_time, observation_duration)

        uvws = self.projected_baselines(baselines, time_offsets).reshape(
            baselines.shape[0], time_offsets.size, 3)

        # grid each baseline type into uv plane
        dim = len(self.ugrid(bl_max))
        edges = self.ugrid_edges(bl_max)

        uvsum = np.zeros((len(baselines), dim, dim))
        for cnt, (uvw, nbls) in enumerate(
                tqdm.tqdm(
                    zip(uvws, weights),
                    desc="gridding baselines",
                    unit="baselines",
                    disable=not config.PROGRESS,
                    total=len(weights),
                )):
            uvsum[cnt] = np.histogram2d(uvw[:, 0], uvw[:, 1],
                                        bins=edges)[0] * nbls

        return uvsum
Пример #16
0
def test_pickling(ptype_name):
    # Regression test for #11685
    ptype = u.get_physical_type(ptype_name)
    pkl = pickle.dumps(ptype)
    other = pickle.loads(pkl)
    assert other == ptype
Пример #17
0
    def __init__(self, x, y, xUnit, yUnit, params={}):
        '''
        Inputs:
                x = wavelength/frequency values, 1d array-like
                y = spectral flux density, 1d array-like
                xUnit = astropy.units.Unit of physical type length or frequency
                yUnit = astropy.units.Unit of physical type luminosity/flux density /length or frequency

        '''
        self.type = 'spectrum'
        # Validate x input
        wavelengths = np.asarray(x)
        spec = np.asarray(y)
        if wavelengths.ndim != 1:
            raise ValueError(
                'Wavelength/Frequency must be given as a 1d array')
        if wavelengths.shape != spec.shape:
            raise ValueError(
                'Wavelength/Frequency array and flux density array have different shapes.'
            )
        if u.get_physical_type(xUnit) not in ['frequency', 'length']:
            raise TypeError(
                xUnit,
                ' is neither a unit of frequency nor a unit of length. Get it together.'
            )
        wavelengths = wavelengths * xUnit
        wavelengths.to('micron', equivalencies=u.spectral())
        if not np.all(np.ediff1d(wavelengths) > 0.):
            if u.get_physical_type(xUnit) == 'frequency':
                raise ValueError(
                    'Frequencies must be monotonically decreasing.')
            else:
                raise ValueError(
                    'Wavelengths must be monotonically increasing.')
        self.wavelengths = wavelengths

        # Validate y input
        spec = np.asarray(y)
        if spec.ndim != 1:
            raise ValueError('Spectrum must be given as a 1d array')

        # Define desired flux units from base units
        uFnu = u.Unit('erg') / u.Unit('s') / u.Unit('Hz')
        uFnuN = u.Unit('erg') / u.Unit('s') / u.Unit('Hz') / u.Unit(
            'cm')**2  # astropy equivalency only works when area uncluded
        uFlam = u.Unit('erg') / u.Unit('s') / u.Unit('Angstrom')
        uFlamN = u.Unit('erg') / u.Unit('s') / u.Unit('Angstrom') / u.Unit(
            'cm')**2

        # Need F_nu, but if there's no area, need to add because too lazy to add equivalency
        if yUnit.is_equivalent(uFnu):
            spec = spec * yUnit / (4 * np.pi *
                                   ((10 * u.Unit('parsec')).to('cm'))**2)
            #spec = spec * yUnit / u.Unit('m')**2
        elif yUnit.is_equivalent(uFlam):
            spec = spec * yUnit / (4 * np.pi *
                                   ((10 * u.Unit('parsec')).to('cm'))**2)
            #spec = spec * yUnit/ u.Unit('m')**2
        else:
            spec = (spec * yUnit).to(uFnuN)

        if not spec.unit.is_equivalent(uFlamN) and not spec.unit.is_equivalent(
                uFnuN):
            raise ValueError(
                spec.unit,
                ' not recognized as a unit of spectral flux density.')
        spec = spec.to(uFnuN, equivalencies=u.spectral_density(wavelengths))

        if wavelengths[-1] < wavelengths[
                0]:  # then we originally had flux units
            wavelengths = np.flipud(wavelengths)
            spec = np.flipud(spec)
        self.spec = spec
        self.params = params