def __call__(self, duration, samplerate): """Produce simulated voltages for given duration and samplerate Parameters ---------- duration : Quantity Should be time units samplerate : Quantity Rate at which samples should be generated The samples are complex, so the real and imaginary parts can be used as separate time streams, or can be thought of as complex voltages. The total number of samples is duration * samplerate. """ times = (np.arange(0., (duration * samplerate).to(1).value, dtype=np.float32) / samplerate).to(duration.unit) nbins = times.shape[0] spectral_power = (np.arange(0., (duration * samplerate).to(1).value, dtype=np.float32) / (duration * self.nu0)).to(1)**self.spectral_index spectral_power *= self.fnu0.astype(np.float32) spectral_phase = np.random.uniform(size=nbins) * u.cycle with u.add_enabled_equivalencies(u.dimensionless_angles()): spectrum = np.sqrt(spectral_power) * np.exp(1j * spectral_phase) spectrum[0] = 0. return times, ifft(spectrum, overwrite_x=True)
def evaluate(self, in_x): """ GCC09_MWAvg function Parameters ---------- in_x: float expects either x in units of wavelengths or frequency or assumes wavelengths in wavenumbers [1/micron] internally wavenumbers are used Returns ------- axav: np array (float) A(x)/A(V) extinction curve [mag] Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(in_x, 1.0 / u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, x_range_GCC09, 'GCC09') # P92 parameters fit to the data using uncs as weights p92_fit = P92(BKG_amp=203.805939127, BKG_lambda=0.0508199427208, BKG_b=88.0591826413, BKG_n=2.0, FUV_amp=5.33962141873, FUV_lambda=0.08, FUV_b=-0.777129536415, FUV_n=3.88322376926, NUV_amp=0.0447023090042, NUV_lambda=0.217548391182, NUV_b=-1.95723797612, NUV_n=2.0, SIL1_amp=0.00264935064935, SIL1_lambda=9.7, SIL1_b=-1.95, SIL1_n=2.0, SIL2_amp=0.00264935064935, SIL2_lambda=18.0, SIL2_b=-1.80, SIL2_n=2.0, FIR_amp=0.01589610389, FIR_lambda=25.0, FIR_b=0.0, FIR_n=2.0) # return A(x)/A(V) return p92_fit(in_x)
def test_equivalency_context_manager(): base_registry = u.get_current_unit_registry() def just_to_from_units(equivalencies): return [(equiv[0], equiv[1]) for equiv in equivalencies] tf_dimensionless_angles = just_to_from_units(u.dimensionless_angles()) tf_spectral = just_to_from_units(u.spectral()) assert base_registry.equivalencies == [] with u.set_enabled_equivalencies(u.dimensionless_angles()): new_registry = u.get_current_unit_registry() assert (set(just_to_from_units(new_registry.equivalencies)) == set(tf_dimensionless_angles)) assert set(new_registry.all_units) == set(base_registry.all_units) with u.set_enabled_equivalencies(u.spectral()): newer_registry = u.get_current_unit_registry() assert (set(just_to_from_units(newer_registry.equivalencies)) == set(tf_spectral)) assert (set(newer_registry.all_units) == set(base_registry.all_units)) assert (set(just_to_from_units(new_registry.equivalencies)) == set(tf_dimensionless_angles)) assert set(new_registry.all_units) == set(base_registry.all_units) with u.add_enabled_equivalencies(u.spectral()): newer_registry = u.get_current_unit_registry() assert (set(just_to_from_units(newer_registry.equivalencies)) == set(tf_dimensionless_angles) | set(tf_spectral)) assert (set(newer_registry.all_units) == set(base_registry.all_units)) assert base_registry is u.get_current_unit_registry()
def _get_x_in_wavenumbers(in_x): """ Convert input x to wavenumber given x has units. Otherwise, assume x is in waveneumbers and issue a warning to this effect. Parameters ---------- in_x : astropy.quantity or simple floats x values Returns ------- x : floats input x values in wavenumbers w/o units """ # handles the case where x is a scaler in_x = np.atleast_1d(in_x) # check if in_x is an astropy quantity, if not issue a warning if not isinstance(in_x, u.Quantity): warnings.warn("x has no units, assuming x units are inverse microns", UserWarning) # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(in_x, 1.0 / u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients return x_quant.value
def integrate(self, *args): # TODO: Remove unit hardcoding when we use model with units natively. with u.add_enabled_equivalencies(u.spectral()): width = u.Quantity(self.width, u.AA) slope = u.Quantity(self.slope, 1 / u.AA) return self.amplitude * (width + self.amplitude / slope)
def integrate(self, x): # TODO: Remove unit hardcoding when we use model with units natively. with u.add_enabled_equivalencies(u.spectral()): x = u.Quantity(x, u.AA) x_0 = u.Quantity(self.x_0, u.AA) sig = u.Quantity(self.sigma, u.AA) # Roots, where y=0 root_left = x_0 - sig root_right = x_0 + sig x_min = min(x) x_max = max(x) if x_min >= root_left or x_max <= root_right: raise NotImplementedError( 'Partial analytic integration not supported') sig2 = sig * sig def _int_subregion(xx1, xx2): dx_min = xx1 - x_0 dx_max = xx2 - x_0 a1 = dx_min * np.exp(-0.5 * dx_min * dx_min / sig2) a2 = dx_max * np.exp(-0.5 * dx_max * dx_max / sig2) return abs(a2 - a1) # Unsigned area return self.amplitude * (_int_subregion(x_min, root_left) + _int_subregion(root_left, root_right) + _int_subregion(root_right, x_max))
def test_dimensionless_redshift(): """Test :func:`astropy.cosmology.units.dimensionless_redshift`.""" z = 3 * cu.redshift val = 3 * u.one # show units not equal assert z.unit == cu.redshift assert z.unit != u.one # test equivalency enabled by default assert z == val # also test that it works for powers assert (3 * cu.redshift ** 3) == val # and in composite units assert (3 * u.km / cu.redshift ** 3) == 3 * u.km # test it also works as an equivalency with u.set_enabled_equivalencies([]): # turn off default equivalencies assert z.to(u.one, equivalencies=cu.dimensionless_redshift()) == val with pytest.raises(ValueError): z.to(u.one) # if this fails, something is really wrong with u.add_enabled_equivalencies(cu.dimensionless_redshift()): assert z == val
def test_equivalency_context_manager(): base_registry = u.get_current_unit_registry() def just_to_from_units(equivalencies): return [(equiv[0], equiv[1]) for equiv in equivalencies] tf_dimensionless_angles = just_to_from_units(u.dimensionless_angles()) tf_spectral = just_to_from_units(u.spectral()) assert base_registry.equivalencies == [] with u.set_enabled_equivalencies(u.dimensionless_angles()): new_registry = u.get_current_unit_registry() assert (set(just_to_from_units(new_registry.equivalencies)) == set(tf_dimensionless_angles)) assert set(new_registry.all_units) == set(base_registry.all_units) with u.set_enabled_equivalencies(u.spectral()): newer_registry = u.get_current_unit_registry() assert (set(just_to_from_units(newer_registry.equivalencies)) == set(tf_spectral)) assert (set(newer_registry.all_units) == set(base_registry.all_units)) assert (set(just_to_from_units(new_registry.equivalencies)) == set(tf_dimensionless_angles)) assert set(new_registry.all_units) == set(base_registry.all_units) with u.add_enabled_equivalencies(u.spectral()): newer_registry = u.get_current_unit_registry() assert (set(just_to_from_units(newer_registry.equivalencies)) == set(tf_dimensionless_angles) | set(tf_spectral)) assert (set(newer_registry.all_units) == set(base_registry.all_units)) assert base_registry is u.get_current_unit_registry()
def __call__(self, duration, samplerate): """Produce simulated voltages for given duration and samplerate Parameters ---------- duration : Quantity Should be time units samplerate : Quantity Rate at which samples should be generated The samples are complex, so the real and imaginary parts can be used as separate time streams, or can be thought of as complex voltages. The total number of samples is duration * samplerate. """ times = (np.arange(0., (duration * samplerate).to(1).value, dtype=np.float32) / samplerate).to(duration.unit) nbins = times.shape[0] spectral_power = (np.arange(0., (duration * samplerate).to(1).value, dtype=np.float32) / (duration * self.nu0)).to(1) ** self.spectral_index spectral_power *= self.fnu0.astype(np.float32) spectral_phase = np.random.uniform(size=nbins) * u.cycle with u.add_enabled_equivalencies(u.dimensionless_angles()): spectrum = np.sqrt(spectral_power) * np.exp(1j * spectral_phase) spectrum[0] = 0. return times, ifft(spectrum, overwrite_x=True)
def blackbody_lambda(wavelength, temperature): """ Calculate the blackbody spectral density per unit wavelength. Parameters ---------- wavelength : `~astropy.units.Quantity` Wavelength array to evaluate on. temperature : `~astropy.units.Quantity` Blackbody temperature. """ # Convert to units for calculations, also force double precision with u.add_enabled_equivalencies(u.spectral() + u.temperature()): freq = u.Quantity(wavelength, u.Hz, dtype=np.float64) temp = u.Quantity(temperature, u.K, dtype=np.float64) log_boltz = const.h * freq / (const.k_B * temp) boltzm1 = np.expm1(log_boltz) bb_nu = (2.0 * const.h * freq ** 3 / (const.c ** 2 * boltzm1)) flam = u.erg / (u.cm**2 * u.s * u.AA) flux = bb_nu.to(flam, u.spectral_density(wavelength)) return flux / u.sr # Add per steradian to output flux unit
def test_redlaw_call(self): w = self.redlaw.waveset[48:53] with add_enabled_equivalencies(u.spectral()): assert_quantity_allclose( w, [5.41599989, 5.3204999, 5.2249999, 5.12949991, 5.03399992] * (u.micron ** -1)) assert_quantity_allclose( self.redlaw(w), [7.90572977, 8.01734924, 8.17892265, 8.40150452, 8.69231796])
def k_lambda(self, x): """ Compute the starburst reddening curve of Calzetti et al. (2000) k'(λ)=A(λ)/E(B-V) Parameters ---------- in_x: float expects either x in units of wavelengths or frequency or assumes wavelengths in [micron] internally microns are used Returns ------- k_lambda: np array (float) k_lambda(x) reddening curve Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(x, u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, self.x_range, "C00") # setup the ax vectors n_x = len(x) axEbv = np.zeros(n_x) # define the ranges uv2vis_indxs = np.where(np.logical_and(0.12 <= x, x < 0.63)) nir_indxs = np.where(np.logical_and(0.63 <= x, x < self.x_range[1])) axEbv[uv2vis_indxs] = ( 2.659 * ( -2.156 + 1.509 * 1 / x[uv2vis_indxs] - 0.198 * 1 / x[uv2vis_indxs] ** 2 + 0.011 * 1 / x[uv2vis_indxs] ** 3 ) + self.Rv ) axEbv[nir_indxs] = 2.659 * (-1.857 + 1.040 * 1 / x[nir_indxs]) + self.Rv return _positive_klambda(axEbv)
def _validate_with_unit(cosmology, param, value): """ Default Parameter value validator. Adds/converts units if Parameter has a unit. """ if param.unit is not None: with u.add_enabled_equivalencies(param.equivalencies): value = u.Quantity(value, param.unit) return value
def integrate(self, x): # TODO: Remove unit hardcoding when we use model with units natively. with u.add_enabled_equivalencies(u.spectral()): x = u.Quantity(x, u.AA) x_0 = u.Quantity(self.x_0, u.AA) amp = u.Quantity(self.amplitude, self._flux_unit) fac = 1 - self.alpha denom = x_0**-self.alpha * fac return amp * (max(x)**fac - min(x)**fac) / denom
def evaluate(x, Av): """ C00 function Parameters ---------- in_x: float expects either x in units of wavelengths or frequency or assumes wavelengths in [micron] internally microns are used Returns ------- att: np array (float) Att(x) attenuation curve [mag] Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(x, u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, x_range_C00, 'C00') # setup the ax vectors n_x = len(x) axEbv = np.zeros(n_x) # Rv is fixed to 4.05 Rv = 4.05 # define the ranges uv2vis_indxs = np.where(np.logical_and(0.12 <= x, x < 0.63)) nir_indxs = np.where(np.logical_and(0.63 <= x, x < 2.2)) axEbv[uv2vis_indxs] = (2.659 * (-2.156 + 1.509 * 1 / x[uv2vis_indxs] - 0.198 * 1 / x[uv2vis_indxs] ** 2 + 0.011 * 1 / x[uv2vis_indxs] ** 3) + Rv) axEbv[nir_indxs] = 2.659 * (-1.857 + 1.040 * 1 / x[nir_indxs]) + Rv ax = axEbv / Rv * Av return ax
def evaluate(self, in_x, BKG_amp, BKG_lambda, BKG_b, BKG_n, FUV_amp, FUV_lambda, FUV_b, FUV_n, NUV_amp, NUV_lambda, NUV_b, NUV_n, SIL1_amp, SIL1_lambda, SIL1_b, SIL1_n, SIL2_amp, SIL2_lambda, SIL2_b, SIL2_n, FIR_amp, FIR_lambda, FIR_b, FIR_n): """ P92 function Parameters ---------- in_x: float expects either x in units of wavelengths or frequency or assumes wavelengths in wavenumbers [1/micron] internally wavenumbers are used Returns ------- axav: np array (float) A(x)/A(V) extinction curve [mag] Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(in_x, 1.0/u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, x_range_P92, 'P92') # calculate the terms lam = 1.0/x axav = (self._p92_single_term(lam, BKG_amp, BKG_lambda, BKG_b, BKG_n) + self._p92_single_term(lam, FUV_amp, FUV_lambda, FUV_b, FUV_n) + self._p92_single_term(lam, NUV_amp, NUV_lambda, NUV_b, NUV_n) + self._p92_single_term(lam, SIL1_amp, SIL1_lambda, SIL1_b, SIL1_n) + self._p92_single_term(lam, SIL2_amp, SIL2_lambda, SIL2_b, SIL2_n) + self._p92_single_term(lam, FIR_amp, FIR_lambda, FIR_b, FIR_n)) # return A(x)/A(V) return axav
def wrapper(*func_args, **func_kwargs): # Bind the arguments to our new function to the signature of the original. bound_args = wrapped_signature.bind(*func_args, **func_kwargs) # Iterate through the parameters of the original signature for param in wrapped_signature.parameters.values(): # We do not support variable arguments (*args, # **kwargs) if param.kind in (funcsigs.Parameter.VAR_KEYWORD, funcsigs.Parameter.VAR_POSITIONAL): continue # Catch the (never triggered) case where bind relied on a default value. if param.name not in bound_args.arguments and param.default is not param.empty: bound_args.arguments[param.name] = param.default # Get the value of this parameter (argument to new function) arg = bound_args.arguments[param.name] # Get target unit, either from decotrator kwargs or annotations if param.name in self.decorator_kwargs: target_unit = self.decorator_kwargs[param.name] else: target_unit = param.annotation # If the target unit is empty, then no unit was specified so we # move past it if target_unit is not funcsigs.Parameter.empty: try: equivalent = arg.unit.is_equivalent( target_unit, equivalencies=self.equivalencies) if not equivalent: raise UnitsError("Argument '{0}' to function '{1}'" " must be in units convertable to" " '{2}'.".format( param.name, wrapped_function.__name__, target_unit.to_string())) # Either there is no .unit or no .is_equivalent except AttributeError: if hasattr(arg, "unit"): error_msg = "a 'unit' attribute without an 'is_equivalent' method" else: error_msg = "no 'unit' attribute" raise TypeError( "Argument '{0}' to function has '{1}' {2}. " "You may want to pass in an astropy Quantity instead." .format(param.name, wrapped_function.__name__, error_msg)) # Call the original function with any equivalencies in force. with add_enabled_equivalencies(self.equivalencies): return wrapped_function(*func_args, **func_kwargs)
def blackbody_nu(in_x, temperature): """Calculate blackbody flux per steradian, :math:`B_{\\nu}(T)`. .. note:: Use `numpy.errstate` to suppress Numpy warnings, if desired. .. warning:: Output values might contain ``nan`` and ``inf``. Parameters ---------- in_x : number, array-like, or `~astropy.units.Quantity` Frequency, wavelength, or wave number. If not a Quantity, it is assumed to be in Hz. temperature : number or `~astropy.units.Quantity` Blackbody temperature. If not a Quantity, it is assumed to be in Kelvin. Returns ------- flux : `~astropy.units.Quantity` Blackbody monochromatic flux in :math:`erg \\; cm^{-2} s^{-1} Hz^{-1} sr^{-1}`. Raises ------ ValueError Invalid temperature. ZeroDivisionError Wavelength is zero (when converting to frequency). """ # Convert to units for calculations, also force double precision with u.add_enabled_equivalencies(u.spectral() + u.temperature()): freq = u.Quantity(in_x, u.Hz, dtype=np.float64) temp = u.Quantity(temperature, u.K, dtype=np.float64) # Check if input values are physically possible if temp < 0: raise ValueError('Invalid temperature {0}'.format(temp)) if np.any(freq <= 0): # pragma: no cover warnings.warn('Input contains invalid wavelength/frequency value(s)', AstropyUserWarning) # Calculate blackbody flux bb_nu = (2.0 * const.h * freq**3 / (const.c**2 * np.expm1(const.h * freq / (const.k_B * temp)))) flux = bb_nu.to(FNU, u.spectral_density(freq)) return flux / u.sr # Add per steradian to output flux unit
def integrate(self, x): # TODO: Remove unit hardcoding when we use model with units natively. with u.add_enabled_equivalencies(u.spectral()): x = u.Quantity(x, u.AA) x_0 = u.Quantity(self.x_0, u.AA) gamma = u.Quantity(self.fwhm, u.AA) * 0.5 a1 = np.arctan((min(x) - x_0) / gamma) a2 = np.arctan((max(x) - x_0) / gamma) da = (a2 - a1).to(u.dimensionless_unscaled, u.dimensionless_angles()) return self.amplitude * gamma * da
def blackbody_nu(in_x, temperature): """Calculate blackbody flux per steradian, :math:`B_{\\nu}(T)`. .. note:: Use `numpy.errstate` to suppress Numpy warnings, if desired. .. warning:: Output values might contain ``nan`` and ``inf``. Parameters ---------- in_x : number, array-like, or `~astropy.units.Quantity` Frequency, wavelength, or wave number. If not a Quantity, it is assumed to be in Hz. temperature : number or `~astropy.units.Quantity` Blackbody temperature. If not a Quantity, it is assumed to be in Kelvin. Returns ------- flux : `~astropy.units.Quantity` Blackbody monochromatic flux in :math:`erg \\; cm^{-2} s^{-1} Hz^{-1} sr^{-1}`. Raises ------ ValueError Invalid temperature. ZeroDivisionError Wavelength is zero (when converting to frequency). """ # Convert to units for calculations, also force double precision with u.add_enabled_equivalencies(u.spectral() + u.temperature()): freq = u.Quantity(in_x, u.Hz, dtype=np.float64) temp = u.Quantity(temperature, u.K, dtype=np.float64) # Check if input values are physically possible if temp < 0: raise ValueError('Invalid temperature {0}'.format(temp)) if np.any(freq <= 0): # pragma: no cover warnings.warn('Input contains invalid wavelength/frequency value(s)', AstropyUserWarning) # Calculate blackbody flux bb_nu = (2.0 * const.h * freq ** 3 / (const.c ** 2 * np.expm1(const.h * freq / (const.k_B * temp)))) flux = bb_nu.to(FNU, u.spectral_density(freq)) return flux / u.sr # Add per steradian to output flux unit
def integrate(self, x): # TODO: Remove unit hardcoding when we use model with units natively. # TODO: We do not handle wav_unit as wave number nor energy for now. if 'wav' in self._flux_unit.physical_type: wav_unit = u.AA else: wav_unit = u.Hz with u.add_enabled_equivalencies(u.spectral()): x = u.Quantity(x, wav_unit) amp = u.Quantity(self.amplitude, self._flux_unit) return (max(x) - min(x)) * amp
def blackbody_nu(in_x, temperature): """Calculate blackbody flux per steradian, :math:`B_{\\nu}(T)`. .. note:: Use `numpy.errstate` to suppress Numpy warnings, if desired. .. warning:: Output values might contain ``nan`` and ``inf``. Parameters in_x : number, array-like, or `~astropy.units.Quantity` Frequency, wavelength, or wave number. If not a Quantity, it is assumed to be in Hz. temperature : number, array-like, or `~astropy.units.Quantity` Blackbody temperature. If not a Quantity, it is assumed to be in Kelvin. Returns flux : `~astropy.units.Quantity` Blackbody monochromatic flux in :math:`erg \\; cm^{-2} s^{-1} Hz^{-1} sr^{-1}`. Raises ValueError Invalid temperature. ZeroDivisionError Wavelength is zero (when converting to frequency). """ FNU = u.erg / (u.cm**2 * u.s * u.Hz) FLAM = u.erg / (u.cm**2 * u.s * u.AA) # Convert to units for calculations, also force double precision with u.add_enabled_equivalencies(u.spectral() + u.temperature()): freq = u.Quantity(in_x, u.Hz, dtype=np.float64) temp = u.Quantity(temperature, u.K, dtype=np.float64) log_boltz = const.h * freq / (const.k_B * temp) boltzm1 = np.expm1(log_boltz) # Calculate blackbody flux bb_nu = (2.0 * const.h * freq**3 / (const.c**2 * boltzm1)) flux = bb_nu.to(FNU, u.spectral_density(freq)) return flux / u.sr # Add per steradian to output flux unit
def evaluate(in_x, RvA, fA): """ G16 function Parameters ---------- in_x: float expects either x in units of wavelengths or frequency or assumes wavelengths in wavenumbers [1/micron] internally wavenumbers are used Returns ------- axav: np array (float) A(x)/A(V) extinction curve [mag] Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(in_x, 1.0/u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, x_range_G16, 'G16') # ensure Rv is a single element, not numpy array RvA = RvA[0] # get the A component extinction model extA_model = F99(Rv=RvA) alav_A = extA_model(x) # get the B component extinction model extB_model = G03_SMCBar() alav_B = extB_model(x) # create the mixture model alav = fA*alav_A + (1.0 - fA)*alav_B # return A(x)/A(V) return alav
def evaluate(in_x, C1, C2, C3, C4, xo, gamma): """ FM90 function Parameters ---------- in_x: float expects either x in units of wavelengths or frequency or assumes wavelengths in wavenumbers [1/micron] internally wavenumbers are used Returns ------- exvebv: np array (float) E(x-V)/E(B-V) extinction curve [mag] Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(in_x, 1.0/u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, x_range_FM90, 'FM90') # linear term exvebv = C1 + C2*x # bump term x2 = x**2 exvebv += C3*(x2/((x2 - xo**2)**2 + x2*(gamma**2))) # FUV rise term fnuv_indxs = np.where(x >= 5.9) if len(fnuv_indxs) > 0: y = x[fnuv_indxs] - 5.9 exvebv[fnuv_indxs] += C4*(0.5392*(y**2) + 0.05644*(y**3)) # return E(x-V)/E(B-V) return exvebv
def test_one_argument_ufunc_at(self): q = np.arange(10.) * u.m i = np.array([1, 2]) qv = q.value.copy() np.negative.at(q, i) np.negative.at(qv, i) assert np.all(q.value == qv) assert q.unit is u.m # cannot change from quantity to bool array with pytest.raises(TypeError): np.isfinite.at(q, i) # for selective in-place, cannot change the unit with pytest.raises(u.UnitsError): np.square.at(q, i) # except if the unit does not change (i.e., dimensionless) d = np.arange(10.) * u.dimensionless_unscaled dv = d.value.copy() np.square.at(d, i) np.square.at(dv, i) assert np.all(d.value == dv) assert d.unit is u.dimensionless_unscaled d = np.arange(10.) * u.dimensionless_unscaled dv = d.value.copy() np.log.at(d, i) np.log.at(dv, i) assert np.all(d.value == dv) assert d.unit is u.dimensionless_unscaled # also for sine it doesn't work, even if given an angle a = np.arange(10.) * u.radian with pytest.raises(u.UnitsError): np.sin.at(a, i) # except, for consistency, if we have made radian equivalent to # dimensionless (though hopefully it will never be needed) av = a.value.copy() with u.add_enabled_equivalencies(u.dimensionless_angles()): np.sin.at(a, i) np.sin.at(av, i) assert_allclose(a.value, av) # but we won't do double conversion ad = np.arange(10.) * u.degree with pytest.raises(u.UnitsError): np.sin.at(ad, i)
def test_one_argument_ufunc_at(self): q = np.arange(10.) * u.m i = np.array([1, 2]) qv = q.value.copy() np.negative.at(q, i) np.negative.at(qv, i) assert np.all(q.value == qv) assert q.unit is u.m # cannot change from quantity to bool array with pytest.raises(TypeError): np.isfinite.at(q, i) # for selective in-place, cannot change the unit with pytest.raises(u.UnitsError): np.square.at(q, i) # except if the unit does not change (i.e., dimensionless) d = np.arange(10.) * u.dimensionless_unscaled dv = d.value.copy() np.square.at(d, i) np.square.at(dv, i) assert np.all(d.value == dv) assert d.unit is u.dimensionless_unscaled d = np.arange(10.) * u.dimensionless_unscaled dv = d.value.copy() np.log.at(d, i) np.log.at(dv, i) assert np.all(d.value == dv) assert d.unit is u.dimensionless_unscaled # also for sine it doesn't work, even if given an angle a = np.arange(10.) * u.radian with pytest.raises(u.UnitsError): np.sin.at(a, i) # except, for consistency, if we have made radian equivalent to # dimensionless (though hopefully it will never be needed) av = a.value.copy() with u.add_enabled_equivalencies(u.dimensionless_angles()): np.sin.at(a, i) np.sin.at(av, i) assert_allclose(a.value, av) # but we won't do double conversion ad = np.arange(10.) * u.degree with pytest.raises(u.UnitsError): np.sin.at(ad, i)
def evaluate(self, x, tau_V): """ WG00 function Parameters ---------- x: float expects either x in units of wavelengths or frequency or assumes wavelengths in [micron] internally microns are used tau_V: float optical depth in V band Returns ------- Attx: np array (float) Att(x) attenuation curve [mag] Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(x, u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, x_range_WG00, 'WG00') # setup the ax vectors n_x = len(x) xinterp = 1e4 * x yinterp = tau_V * np.ones(n_x) taux = self.model(xinterp, yinterp) # Convert optical depth to attenuation Attx = 1.086 * taux return Attx
def get_fesc(self, x, tau_V): """ Return the total escaping flux fraction at a given wavelength and V-band optical depth. Parameters ---------- x: float expects either x in units of wavelengths or frequency or assumes wavelengths in [micron] internally microns are used tau_V: float optical depth in V band Returns ------- fsca: np array (float) fsca(x) scattered flux fraction Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(x, u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, x_range_WG00, 'WG00') # setup the ax vectors x = np.atleast_1d(x) n_x = len(x) xinterp = 1e4 * x yinterp = tau_V * np.ones(n_x) return self.fesc(xinterp, yinterp)
def test_ilshift_magnitude(self): # test in-place operation and conversion mag_fnu_cgs = u.mag(u.erg/u.s/u.cm**2/u.Hz) m = np.arange(10.0) * u.mag(u.Jy) jy = m.physical m2 = m << mag_fnu_cgs assert np.all(m2 == m.to(mag_fnu_cgs)) m2 = m m <<= mag_fnu_cgs assert m is m2 # Check it was done in-place! assert np.all(m.value == m2.value) assert m.unit == mag_fnu_cgs # Check it works if equivalencies are in-place. with u.add_enabled_equivalencies(u.spectral_density(5500*u.AA)): st = jy.to(u.ST) m <<= u.STmag assert m is m2 assert_quantity_allclose(m.physical, st) assert m.unit == u.STmag
def test_ilshift_magnitude(self): # test in-place operation and conversion mag_fnu_cgs = u.mag(u.erg / u.s / u.cm**2 / u.Hz) m = np.arange(10.0) * u.mag(u.Jy) jy = m.physical m2 = m << mag_fnu_cgs assert np.all(m2 == m.to(mag_fnu_cgs)) m2 = m m <<= mag_fnu_cgs assert m is m2 # Check it was done in-place! assert np.all(m.value == m2.value) assert m.unit == mag_fnu_cgs # Check it works if equivalencies are in-place. with u.add_enabled_equivalencies(u.spectral_density(5500 * u.AA)): st = jy.to(u.ST) m <<= u.STmag assert m is m2 assert_quantity_allclose(m.physical, st) assert m.unit == u.STmag
def __call__(self, t): """Compute the apparent phase at one or more times. Parameters ---------- t : `~astropy.time.Time` The input time stamps. Returns ------- phase : `~baseband_tasks.phases.Phase` The apparent pulse phase at time ``t``, using a 2-part double of the integer cycle and the fractional phase. The latter is between -0.5 and 0.5. """ toas = self.toa_maker(t) ph = self.model.phase(toas) shape = getattr(toas, 'shape', ()) # TODO: Once PINT uses the Phase class, we can return the # result directly. with u.add_enabled_equivalencies([(u.dimensionless_unscaled, u.cy)]): return Phase(ph.int, ph.frac).reshape(shape)
def k_lambda(self, x): """ Compute the starburst reddening curve of Leitherer et al. (2002) k'(λ)=A(λ)/E(B-V) Parameters ---------- in_x: float expects either x in units of wavelengths or frequency or assumes wavelengths in [micron] internally microns are used Returns ------- k_lambda: np array (float) k_lambda(x) reddening curve Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(x, u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, x_range_L02, 'L02') axEbv = (5.472 + (0.671 * 1 / x - 9.218 * 1e-3 / x**2 + 2.620 * 1e-3 / x**3)) return axEbv
def evaluate(self, x, Av): """ Returns the attenuation curve, A(λ), following the recipe of Leitherer et al. (2002), assuming Rv=4.05 Parameters ---------- in_x: float expects either x in units of wavelengths or frequency or assumes wavelengths in [micron] internally microns are used Returns ------- att: np array (float) Att(x) attenuation curve [mag] Raises ------ ValueError Input x values outside of defined range """ # convert to wavenumbers (1/micron) if x input in units # otherwise, assume x in appropriate wavenumber units with u.add_enabled_equivalencies(u.spectral()): x_quant = u.Quantity(x, u.micron, dtype=np.float64) # strip the quantity to avoid needing to add units to all the # polynomical coefficients x = x_quant.value # check that the wavenumbers are within the defined range _test_valid_x_range(x, self.x_range, "L02") ax = self.k_lambda(x) / self.Rv * Av return ax
Returns ------- equivalency : list The returned list contains one tuple with the equivalency. ''' return [( apu.uV / apu.m, (apu.uV / apu.m) ** 2, lambda x: x ** 2, lambda x: x ** 0.5 )] # apu.add_enabled_equivalencies(apu.logarithmic()) apu.add_enabled_equivalencies(efield_equivalency()) # define some useful constants R0 = ( 1. * (con.mu0 / con.eps0) ** 0.5 ).to(apu.Ohm) Erx_unit = ( (1 * apu.W / 4. / np.pi * R0) ** 0.5 / (1 * apu.km) ).to(apu.uV / apu.m) C_VALUE = con.c.to(apu.m / apu.s).value R0_VALUE = R0.to(apu.Ohm).value KB_VALUE = con.k_B.to(apu.J / apu.K).value ERX_VALUE = Erx_unit.to(apu.V / apu.m).value @utils.ranged_quantity_input(
`None` (default), use the ``H0`` attribute from :mod:`~astropy.cosmology.default_cosmology`. References ---------- For an illuminating discussion on why you may or may not want to use little-h at all, see https://arxiv.org/pdf/1308.4150.pdf """ if H0 is None: from .realizations import default_cosmology H0 = default_cosmology.get().H0 h100_val_unit = u.Unit(100 / (H0.to_value(u.km / u.s / u.Mpc)) * littleh) return u.Equivalency([(h100_val_unit, None)], "with_H0", kwargs={"H0": H0}) # =================================================================== # Enable the set of default equivalencies. # If the cosmology package is imported, this is added to the list astropy-wide. u.add_enabled_equivalencies(dimensionless_redshift()) # ============================================================================= # DOCSTRING # This generates a docstring for this module that describes all of the # standard units defined here. if __doc__ is not None: __doc__ += _generate_unit_summary(_ns)
def blackbody_nu(in_x, temperature): """Calculate blackbody flux per steradian, :math:`B_{\\nu}(T)`. .. note:: Use `numpy.errstate` to suppress Numpy warnings, if desired. .. warning:: Output values might contain ``nan`` and ``inf``. Parameters ---------- in_x : number, array-like, or `~astropy.units.Quantity` Frequency, wavelength, or wave number. If not a Quantity, it is assumed to be in Hz. temperature : number, array-like, or `~astropy.units.Quantity` Blackbody temperature. If not a Quantity, it is assumed to be in Kelvin. Returns ------- flux : `~astropy.units.Quantity` Blackbody monochromatic flux in :math:`erg \\; cm^{-2} s^{-1} Hz^{-1} sr^{-1}`. Raises ------ ValueError Invalid temperature. ZeroDivisionError Wavelength is zero (when converting to frequency). """ # Convert to units for calculations, also force double precision with u.add_enabled_equivalencies(u.spectral() + u.temperature()): freq = u.Quantity(in_x, u.Hz, dtype=np.float64) temp = u.Quantity(temperature, u.K, dtype=np.float64) # Check if input values are physically possible if np.any(temp < 0): raise ValueError(f'Temperature should be positive: {temp}') if not np.all(np.isfinite(freq)) or np.any(freq <= 0): warnings.warn('Input contains invalid wavelength/frequency value(s)', AstropyUserWarning) log_boltz = const.h * freq / (const.k_B * temp) boltzm1 = np.expm1(log_boltz) if _has_buggy_expm1: # Replace incorrect nan results with infs--any result of 'nan' is # incorrect unless the input (in log_boltz) happened to be nan to begin # with. (As noted in #4393 ideally this would be replaced by a version # of expm1 that doesn't have this bug, rather than fixing incorrect # results after the fact...) boltzm1_nans = np.isnan(boltzm1) if np.any(boltzm1_nans): if boltzm1.isscalar and not np.isnan(log_boltz): boltzm1 = np.inf else: boltzm1[np.where(~np.isnan(log_boltz) & boltzm1_nans)] = np.inf # Calculate blackbody flux bb_nu = (2.0 * const.h * freq ** 3 / (const.c ** 2 * boltzm1)) flux = bb_nu.to(FNU, u.spectral_density(freq)) return flux / u.sr # Add per steradian to output flux unit
def blackbody_nu(in_x, temperature): """Calculate blackbody flux per steradian, :math:`B_{\\nu}(T)`. .. note:: Use `numpy.errstate` to suppress Numpy warnings, if desired. .. warning:: Output values might contain ``nan`` and ``inf``. Parameters ---------- in_x : number, array-like, or `~astropy.units.Quantity` Frequency, wavelength, or wave number. If not a Quantity, it is assumed to be in Hz. temperature : number, array-like, or `~astropy.units.Quantity` Blackbody temperature. If not a Quantity, it is assumed to be in Kelvin. Returns ------- flux : `~astropy.units.Quantity` Blackbody monochromatic flux in :math:`erg \\; cm^{-2} s^{-1} Hz^{-1} sr^{-1}`. Raises ------ ValueError Invalid temperature. ZeroDivisionError Wavelength is zero (when converting to frequency). """ # Convert to units for calculations, also force double precision with u.add_enabled_equivalencies(u.spectral() + u.temperature()): freq = u.Quantity(in_x, u.Hz, dtype=np.float64) temp = u.Quantity(temperature, u.K, dtype=np.float64) # Check if input values are physically possible if np.any(temp < 0): raise ValueError('Temperature should be positive: {0}'.format(temp)) if not np.all(np.isfinite(freq)) or np.any(freq <= 0): warnings.warn('Input contains invalid wavelength/frequency value(s)', AstropyUserWarning) log_boltz = const.h * freq / (const.k_B * temp) boltzm1 = np.expm1(log_boltz) if _has_buggy_expm1: # Replace incorrect nan results with infs--any result of 'nan' is # incorrect unless the input (in log_boltz) happened to be nan to begin # with. (As noted in #4393 ideally this would be replaced by a version # of expm1 that doesn't have this bug, rather than fixing incorrect # results after the fact...) boltzm1_nans = np.isnan(boltzm1) if np.any(boltzm1_nans): if boltzm1.isscalar and not np.isnan(log_boltz): boltzm1 = np.inf else: boltzm1[np.where(~np.isnan(log_boltz) & boltzm1_nans)] = np.inf # Calculate blackbody flux bb_nu = (2.0 * const.h * freq ** 3 / (const.c ** 2 * boltzm1)) flux = bb_nu.to(FNU, u.spectral_density(freq)) return flux / u.sr # Add per steradian to output flux unit
def __init__(self, uvfitsfile, telescope=None, vsys=0, distance=0, endian=None, **kwargs): """ Reads the uvfits and calculates useful things, e.g. u,v,w, phase and amplitude .byteswap().newbyteorder() is applied in various places to convert to little endian """ f = pfopen(uvfitsfile, **kwargs) self.loadendian = endian if f[0].header['NAXIS1'] != 0: print "error: this file may not be a UV FITS." raise FileError('File format error.') #~ f.info() try: self.hdu = f[0] except: print "error: cannot open uv data HDU." self.hdr = self.hdu.header self.data = self.hdu.data if self.hdr['NAXIS4'] > 1: self.datatype = ('CUBE', 3) else: self.datatype = ('IMAGE', 2) # find spectral axis axis_types = self.WCS.get_axis_types() ax_types = np.array([i['coordinate_type'] for i in axis_types]) try: spec_axis = ('spectral' == ax_types).nonzero()[0][0] freq = self.hdu.header['CRVAL{0}'.format(spec_axis+1)] # assumes the frequency given in Hz self.freq = freq * un.Hz except (IndexError): print('No spectral axis in header.') spec_axis = -1 self.freq = None if 'RESTFREQ' in self.hdu.header.keys(): self.restfreq = self.hdu.header['RESTFREQ'] self.restfreq_unit = self.hdu.header['RESTFREQ'] * u.Hz else: raise StandardError('No restfrequency found, NEED it!') #TODO : Read in velocity and frequency array if present """ The standard unit is to give UU and VV in seconds (??!?) So we have to convert to whatever we want. """ # standard storing unit here is kilo-lambdas # save a million lines of code! u.add_enabled_equivalencies(lambdas_equivalencies(self.restfreq_unit)) self.u = (self.data.par('UU') * u.s).to(klambdas) self.v = (self.data.par('VV') * u.s).to(klambdas) self.w = (self.data.par('WW') * u.s).to(klambdas) self.uvdist = sqrt(self.u.value**2 + self.v.value**2) * klambdas # BASELINE self.baseline = self.hdu.data.par('BASELINE').byteswap().newbyteorder() # DATES self.jdate = self.hdu.data.par('DATE') # set date to 1900, Jan, 01, 01:00:00 if date before before this self.jdate =self.jdate.clip(2415020.5) self.date = _sp.array([jd2gd(i) for i in self.jdate]) self.date0 = self.date.transpose() fields = ['year', 'month', 'day', 'hour', 'minute', 'sec'] self.date1 = {key:value for key,value in zip(fields, self.date0)} # convert to datetime objects # LOSES the sub-second resolution self.date2 = [_dt(int(i[0]), int(i[1]), int(i[2]), int(i[3]), int(i[4]), int(i[5])) for i in self.date] # get number of tracks # TODO : rough hack, separate track if diff day is >1 tmp = _sp.where(_sp.diff(_sp.unique(self.jdate.round(0)))>1)[0] self.ntracks = len(tmp)+1 ################################################################ # NB : need to streamline this. # only load the complex visibilities, into a complex array # AND then work on that # COMPLEX VISIBILITY visi_index = len(self.data.parnames) if self.hdu.header['NAXIS'] == 7: self.visdata = self.data.par(visi_index)[:,0,0,0,0,0,:].byteswap().newbyteorder() #~ self.visdata = self.hdu.data.data[:,0,0,0,0,0,:] elif self.hdu.header['NAXIS'] == 6: self.visdata = self.data.par(visi_index)[:,0,0,0,0,:].byteswap().newbyteorder() # load the re, im and weight arrays self.re, self.im, self.wt = self.visdata[:,:].T #~ self.re = self.visdata[:,0][:] #~ self.im = self.visdata[:,1][:] #~ self.wt = self.visdata[:,2][:] # complex numbers #~ self.comp = self.visdata[:,:2].astype(_np.float64).view(_np.complexfloating) #~ self.comp = 1j*self.visdata[:,1][:] #~ self.comp += self.visdata[:,0][:] #~ self.comp = self.visdata[:,:2].astype(_np.float).view(_np.complex) # below seems a bit dependent... self.cvisi = self.visdata[:,:2].astype(_np.float).view(_np.complex).T[0] """ with complex array, you can do amp = np.abs(vis) np.angle(vis) vis.real vis.imag """ # the data is not shifted self.isshifted = (False, [0,0]) # AMPLITUDE self.amp = sqrt(self.re**2 + self.im**2) # PHASE self.pha = arctan2(self.im, self.re) self.pha_deg = self.pha / pi * 180. # ERROR / SIGMA #TODO : check # following 1.0e6 is just for GILDAS, change if needed #~ print('NB : Error calculated from weights assuming GILDAS ' #~ 'data (i.e. frequencies in MHz).') self.sigma_alt = 1/sqrt(self.wt*1.0e6) # Daniels way of calculating sigma # test this first self.sigma = _sp.sqrt(0.5 / ( self.wt * float(self.amp.shape[0]) ) )