def test_byhand_awav2vel(): # AWAV CRVAL3A = (6560 * u.AA).to(u.m).value CDELT3A = (1.0 * u.AA).to(u.m).value CUNIT3A = 'm' CRPIX3A = 1.0 # restwav MUST be vacuum restwl = air_to_vac(6562.81 * u.AA) RESTWAV = restwl.to(u.m).value CRVAL3V = (CRVAL3A * u.m).to(u.m / u.s, u.doppler_optical(restwl)).value CDELT3V = (CDELT3A * u.m * air_to_vac_deriv(CRVAL3A * u.m) / restwl) * constants.c CUNIT3V = 'm/s' mywcs = wcs.WCS(naxis=1) mywcs.wcs.ctype[0] = 'AWAV' mywcs.wcs.crval[0] = CRVAL3A mywcs.wcs.crpix[0] = CRPIX3A mywcs.wcs.cunit[0] = CUNIT3A mywcs.wcs.cdelt[0] = CDELT3A mywcs.wcs.restwav = RESTWAV mywcs.wcs.set() newwcs = convert_spectral_axis( mywcs, u.km / u.s, determine_ctype_from_vconv(mywcs.wcs.ctype[0], u.km / u.s, 'optical')) newwcs.wcs.set() assert newwcs.wcs.cunit[0] == 'm / s' np.testing.assert_almost_equal( newwcs.wcs.crval, air_to_vac(CRVAL3A * u.m).to(u.m / u.s, u.doppler_optical(restwl)).value) # Check that the cdelts match the expected cdelt, 1 angstrom / rest # wavelength (vac) np.testing.assert_almost_equal(newwcs.wcs.cdelt, CDELT3V.to(u.m / u.s).value) # Check that the reference wavelength is 2.81 angstroms up np.testing.assert_almost_equal(newwcs.wcs_pix2world((2.81, ), 0), 0.0, decimal=3) # Go through a full-on sanity check: vline = 100 * u.km / u.s wave_line_vac = vline.to(u.AA, u.doppler_optical(restwl)) wave_line_air = vac_to_air(wave_line_vac) pix_line_input = mywcs.wcs_world2pix((wave_line_air.to(u.m).value, ), 0) pix_line_output = newwcs.wcs_world2pix((vline.to(u.m / u.s).value, ), 0) np.testing.assert_almost_equal(pix_line_output, pix_line_input, decimal=4)
def test_byhand_awav2vel(): # AWAV CRVAL3A = (6560*u.AA).to(u.m).value CDELT3A = (1.0*u.AA).to(u.m).value CUNIT3A = 'm' CRPIX3A = 1.0 # restwav MUST be vacuum restwl = air_to_vac(6562.81*u.AA) RESTWAV = restwl.to(u.m).value CRVAL3V = (CRVAL3A*u.m).to(u.m/u.s, u.doppler_optical(restwl)).value CDELT3V = (CDELT3A*u.m*air_to_vac_deriv(CRVAL3A*u.m)/restwl) * constants.c CUNIT3V = 'm/s' mywcs = wcs.WCS(naxis=1) mywcs.wcs.ctype[0] = 'AWAV' mywcs.wcs.crval[0] = CRVAL3A mywcs.wcs.crpix[0] = CRPIX3A mywcs.wcs.cunit[0] = CUNIT3A mywcs.wcs.cdelt[0] = CDELT3A mywcs.wcs.restwav = RESTWAV mywcs.wcs.set() newwcs = convert_spectral_axis(mywcs, u.km/u.s, determine_ctype_from_vconv(mywcs.wcs.ctype[0], u.km/u.s, 'optical')) newwcs.wcs.set() assert newwcs.wcs.cunit[0] == 'm / s' np.testing.assert_almost_equal(newwcs.wcs.crval, air_to_vac(CRVAL3A*u.m).to(u.m/u.s, u.doppler_optical(restwl)).value) # Check that the cdelts match the expected cdelt, 1 angstrom / rest # wavelength (vac) np.testing.assert_almost_equal(newwcs.wcs.cdelt, CDELT3V.to(u.m/u.s).value) # Check that the reference wavelength is 2.81 angstroms up np.testing.assert_almost_equal(newwcs.wcs_pix2world((2.81,), 0), 0.0, decimal=3) # Go through a full-on sanity check: vline = 100*u.km/u.s wave_line_vac = vline.to(u.AA, u.doppler_optical(restwl)) wave_line_air = vac_to_air(wave_line_vac) pix_line_input = mywcs.wcs_world2pix((wave_line_air.to(u.m).value,), 0) pix_line_output = newwcs.wcs_world2pix((vline.to(u.m/u.s).value,), 0) np.testing.assert_almost_equal(pix_line_output, pix_line_input, decimal=4)
def Orion_PVDiagrams( filename="OMC1_TSPEC_H2S1_cube.fits", restwavelength=2.1218313 * u.um, cm=pl.cm.hot, start_fignum=0, min_valid=1e-16, displaymax=None, hlcolor="k", linename="H2 S(1) 1-0", dosave=True, ): cube = fits.getdata(filename) header = fits.getheader(filename) wavelength = ((-header["CRPIX3"] + np.arange(header["NAXIS3"]) + 1) * header["CD3_3"] + header["CRVAL3"]) * u.AA velocity = wavelength.to("km/s", u.doppler_optical(restwavelength)) nvel = len(velocity) def make_pv(startx=196, starty=130, endx=267, endy=388, npts=250): pvd = np.empty([nvel, npts]) for ii, (x, y) in enumerate(zip(np.linspace(startx, endx, npts), np.linspace(starty, endy, npts))): pvd[:, ii] = cube[:, y, x] return pvd for ii, (ex, ey) in enumerate(outflow_endpoints): dx = ex - sourceI[0] dy = ey - sourceI[1] angle = np.arctan2(dy, dx) cdelt = np.abs(header["CDELT1"] / np.cos(angle)) * 3600 npts = (dx ** 2 + dy ** 2) ** 0.5 # pixels are in FITS units pv = make_pv(endx=ex - 1, endy=ey - 1, startx=sourceI[0] - 1, starty=sourceI[1] - 1, npts=npts) fignum = start_fignum + ii / 3 pl.figure(fignum) if ii % 3 == 0: pl.clf() ax = pl.subplot(3, 1, ii % 3 + 1) vmin, vmax = velocity.min().value, velocity.max().value pv[pv < 0] = np.nanmin(pv) pv[pv < min_valid] = min_valid pl.imshow( np.log10(pv), extent=[0, npts * cdelt, vmin, vmax], aspect=np.abs(cdelt) / 20 * (npts / 100), cmap=cm, vmax=displaymax, origin="lower", ) pl.hlines(0, 0, npts * cdelt, color=hlcolor, linestyle="--") ax.set_xlabel('Offset (")') ax.set_ylabel("Velocity (km s$^{-1}$)") ax.set_title(linename + " Outflow Trace %i" % ii) ax.set_ylim(-200, 200) if dosave and ii % 3 == 2 or ii == len(outflow_endpoints) - 1: name = linename.replace(" ", "_").replace("(", "_").replace(")", "_") name = "".join([l for l in name if l in (string.ascii_letters + string.digits + "_-")]) figname = name + "_%i.png" % fignum pl.savefig(figname.replace("__", "_"))
def test_with_spectral_unit(self, name, masktype): cube, data = cube_and_raw(name + '.fits') cube_freq = cube.with_spectral_unit(u.Hz) if masktype == BooleanArrayMask: mask = BooleanArrayMask(data>0, wcs=cube._wcs) elif masktype == LazyMask: mask = LazyMask(lambda x: x>0, cube=cube) elif masktype == FunctionMask: mask = FunctionMask(lambda x: x>0) elif masktype == CompositeMask: mask1 = FunctionMask(lambda x: x>0) mask2 = LazyMask(lambda x: x>0, cube) mask = CompositeMask(mask1, mask2) cube2 = cube.with_mask(mask) cube_masked_freq = cube2.with_spectral_unit(u.Hz) assert cube_freq._wcs.wcs.ctype[cube_freq._wcs.wcs.spec] == 'FREQ-W2F' assert cube_masked_freq._wcs.wcs.ctype[cube_masked_freq._wcs.wcs.spec] == 'FREQ-W2F' assert cube_masked_freq._mask._wcs.wcs.ctype[cube_masked_freq._mask._wcs.wcs.spec] == 'FREQ-W2F' # values taken from header rest = 1.42040571841E+09*u.Hz crval = -3.21214698632E+05*u.m/u.s outcv = crval.to(u.m, u.doppler_optical(rest)).to(u.Hz, u.spectral()) assert_allclose(cube_freq._wcs.wcs.crval[cube_freq._wcs.wcs.spec], outcv.to(u.Hz).value) assert_allclose(cube_masked_freq._wcs.wcs.crval[cube_masked_freq._wcs.wcs.spec], outcv.to(u.Hz).value) assert_allclose(cube_masked_freq._mask._wcs.wcs.crval[cube_masked_freq._mask._wcs.wcs.spec], outcv.to(u.Hz).value)
def test_with_spectral_unit(self, name, masktype, unit): cube, data = cube_and_raw(name + '.fits') cube_freq = cube.with_spectral_unit(unit) if masktype == BooleanArrayMask: mask = BooleanArrayMask(data>0, wcs=cube._wcs) elif masktype == LazyMask: mask = LazyMask(lambda x: x>0, cube=cube) elif masktype == FunctionMask: mask = FunctionMask(lambda x: x>0) elif masktype == CompositeMask: mask1 = FunctionMask(lambda x: x>0) mask2 = LazyMask(lambda x: x>0, cube) mask = CompositeMask(mask1, mask2) cube2 = cube.with_mask(mask) cube_masked_freq = cube2.with_spectral_unit(unit) assert cube_freq._wcs.wcs.ctype[cube_freq._wcs.wcs.spec] == 'FREQ-W2F' assert cube_masked_freq._wcs.wcs.ctype[cube_masked_freq._wcs.wcs.spec] == 'FREQ-W2F' assert cube_masked_freq._mask._wcs.wcs.ctype[cube_masked_freq._mask._wcs.wcs.spec] == 'FREQ-W2F' # values taken from header rest = 1.42040571841E+09*u.Hz crval = -3.21214698632E+05*u.m/u.s outcv = crval.to(u.m, u.doppler_optical(rest)).to(u.Hz, u.spectral()) assert_allclose(cube_freq._wcs.wcs.crval[cube_freq._wcs.wcs.spec], outcv.to(u.Hz).value) assert_allclose(cube_masked_freq._wcs.wcs.crval[cube_masked_freq._wcs.wcs.spec], outcv.to(u.Hz).value) assert_allclose(cube_masked_freq._mask._wcs.wcs.crval[cube_masked_freq._mask._wcs.wcs.spec], outcv.to(u.Hz).value)
def calibrate_flux(self, hdu): ''' do flux calibration by undoing what the simulator did so far (as much as possible) input is the result of compute_snr() ''' data = hdu.data * u.electron # per dit, pixel, spectral channel, and M1-area mirr_list = self.cmds.mirrors_telescope mirr_area = np.pi / 4 * np.sum(mirr_list["Outer"]**2 - \ mirr_list["Inner"]**2) * u.m**2 data = data / (hdu.header['EXPTIME'] * u.s * (np.mean(self.wavelen)*u.um * (1.5 *u.km/u.s) / const.c).to(u.um) * mirr_area) # e-/s/um/m2 # wavelengths of data cube det_wavelen = (self.det_velocities * u.m/u.s).to(u.um, equivalencies=u.doppler_optical(self.restcoo)) # interpolate transmission onto wavelength-grid of detector: trans = np.interp(det_wavelen.value, self.wavelen, self.transmission) data /= trans[:, np.newaxis, np.newaxis] data = (data * u.photon/u.electron).to(u.Jy, equivalencies=u.spectral_density(self.restcoo)) data = data / (self.det_pixscale/1000. * u.arcsec)**2 # Jy/arcsec2 calhdu = fits.PrimaryHDU(data.value, header=hdu.header) calhdu.header['BUNIT'] = ('Jy/arcsec2', 'Jansky per arcsec**2') return calhdu
def test_mask_spectral_unit_functions(): cube, data = cube_and_raw('adv.fits') # function mask should do nothing mask1 = FunctionMask(lambda x: x>0) mask_freq1 = mask1.with_spectral_unit(u.Hz) # lazy mask behaves like booleanarraymask mask2 = LazyMask(lambda x: x>0, cube=cube) mask_freq2 = mask2.with_spectral_unit(u.Hz) assert mask_freq2._wcs.wcs.ctype[mask_freq2._wcs.wcs.spec] == 'FREQ-W2F' # values taken from header rest = 1.42040571841E+09*u.Hz crval = -3.21214698632E+05*u.m/u.s outcv = crval.to(u.m, u.doppler_optical(rest)).to(u.Hz, u.spectral()) assert_allclose(mask_freq2._wcs.wcs.crval[mask_freq2._wcs.wcs.spec], outcv.to(u.Hz).value) # again, test that it works mask3 = CompositeMask(mask1,mask2) mask_freq3 = mask3.with_spectral_unit(u.Hz) mask_freq3 = CompositeMask(mask_freq1,mask_freq2) mask_freq_freq3 = mask_freq3.with_spectral_unit(u.Hz)
def test_basic(): """ basic test of equivalent width """ survey = SkySurvey() import astropy.units as u survey["INTEN"] = survey["INTEN"].data * u.R ha_wave = 656.3 * u.nm optical_ha_equiv = u.doppler_optical(ha_wave) dv = (survey["VELOCITY"][0][1] - survey["VELOCITY"][0][0]) * u.km/u.s dlambda = (dv).to(u.AA, equivalencies=optical_ha_equiv) - \ (0 * u.km/u.s).to(u.AA, equivalencies=optical_ha_equiv) survey["BKG"] = np.abs(np.random.randn(len(survey["INTEN"]))) * 100. survey["BKGSD"] = survey["BKG"] * .1 ew, ew_error = survey.get_equivalent_width(continuum = survey["BKG"] / 22.8 * u.R / dlambda, continuum_error = survey["BKGSD"] / 22.8 * u.R / dlambda, return_sigma = True) ew2, ew2_error = survey.get_equivalent_width(intensity = survey["INTEN"].data * u.R, intensity_error = survey["ERROR"].data * u.R) assert np.allclose(ew.value, ew2.value)
def test_convert_to_unit(run_with_assert=False): for from_unit, to_unit, _, __ in u.doppler_optical(1 * u.Hz): sp = SpectroscopicAxis(np.linspace(-100, 100, 100), unit=from_unit, refX=23.2 * u.Hz, velocity_convention='optical') sp.convert_to_unit(to_unit) if (run_with_assert): assert sp.unit == to_unit for from_unit, to_unit, _, __ in u.doppler_radio(1 * u.eV): sp = SpectroscopicAxis(np.linspace(-100, 100, 100), unit=from_unit, refX=23.2 * u.Hz, velocity_convention='optical') sp.convert_to_unit(to_unit) if (run_with_assert): assert sp.unit == to_unit for from_unit, to_unit, _, __ in u.doppler_relativistic(1 * u.Angstrom): sp = SpectroscopicAxis(np.linspace(-100, 100, 100), unit=from_unit, refX=23.2 * u.Hz, velocity_convention='relativistic') sp.convert_to_unit(to_unit) if (run_with_assert): assert sp.unit == to_unit
def test_equivalencies_3(velocity_convention): x = SpectroscopicAxis(np.arange(5), unit=u.angstrom, refX=3, refX_unit='angstrom', velocity_convention=velocity_convention) assert x.equivalencies == u.doppler_optical(3 * u.AA)
def cross_corr(spectrum, template, start_lam=-2, end_lam=2, n_steps=1000, sigma=None, spread_factor=10): """ Cross-correlation of the spectrum and template. Parameters ---------- spectrum : `~hipparchus.Spectrum` Spectrum object (may be an order of an echelle spectrum). template : `~hipparchus.Template` Template object to correlate against the spectrum. start_lam : float Start wavelength relative to mean wavelength end_lam : float End wavelength relative to mean wavelength n_steps : int Number of steps to compute the CCF over between ``start_lam`` and ``end_lam`` sigma : float Gaussian smoothing filter width (standard deviation) spread_factor : float Gaussian smoothing filter width spread scaling factor Returns ------- ccf : `~hipparchus.CCF` Cross-correlation object. """ dx_range = np.linspace(start_lam, end_lam, n_steps)[:, np.newaxis] shifted_wavelengths = spectrum.wavelength - dx_range lam0 = spectrum.wavelength.mean() * u.Angstrom velocities = (lam0 + dx_range * u.Angstrom).to(u.km / u.s, u.doppler_optical(lam0)) if sigma is None: sigma = ((spectrum.wavelength[1] - spectrum.wavelength[0]) / (template.wavelength[1] - template.wavelength[0]) / spread_factor) smoothed_emission = gaussian_filter1d(template.emission, sigma) T = interp1d(template.wavelength, smoothed_emission, bounds_error=False, fill_value=0)(shifted_wavelengths) x = np.repeat(spectrum.flux[:, np.newaxis], n_steps, axis=1).T ccf = np.average(x, weights=T, axis=1) return CCF(velocities, ccf, header=spectrum.header)
def test_compose_equivalencies(): x = u.Unit("arcsec").compose(units=(u.pc,), equivalencies=u.parallax()) assert x[0] == u.pc x = u.Unit("2 arcsec").compose(units=(u.pc,), equivalencies=u.parallax()) assert x[0] == u.Unit(0.5 * u.pc) x = u.degree.compose(equivalencies=u.dimensionless_angles()) assert u.Unit(u.degree.to(u.radian)) in x x = (u.nm).compose(units=(u.m, u.s), equivalencies=u.doppler_optical(0.55*u.micron)) for y in x: if y.bases == [u.m, u.s]: assert y.powers == [1, -1] assert_allclose( y.scale, u.nm.to(u.m / u.s, equivalencies=u.doppler_optical(0.55 * u.micron))) break else: assert False, "Didn't find speed in compose results"
def test_mask_spectral_unit(name): cube, data = cube_and_raw(name + '.fits') mask = BooleanArrayMask(data, cube._wcs) mask_freq = mask.with_spectral_unit(u.Hz) assert mask_freq._wcs.wcs.ctype[mask_freq._wcs.wcs.spec] == 'FREQ-W2F' # values taken from header rest = 1.42040571841E+09*u.Hz crval = -3.21214698632E+05*u.m/u.s outcv = crval.to(u.m, u.doppler_optical(rest)).to(u.Hz, u.spectral()) assert_allclose(mask_freq._wcs.wcs.crval[mask_freq._wcs.wcs.spec], outcv.to(u.Hz).value)
def test_regions_spectral(data_adv): cube, data = cube_and_raw(data_adv) rf_cube = get_rest_value_from_wcs(cube.wcs).to("GHz", equivalencies=u.spectral()) # content of image.reg regpix = regions.RectanglePixelRegion(regions.PixCoord(0.5, 1), width=4, height=2) # Velocity range in doppler_optical same as that of the cube. vel_range_optical = u.Quantity([-318 * u.km / u.s, -320 * u.km / u.s]) regpix.meta['range'] = list(vel_range_optical) sc1 = cube.subcube_from_regions([regpix]) scsum1 = sc1.sum() freq_range = vel_range_optical.to("GHz", equivalencies=u.doppler_optical(rf_cube)) regpix.meta['range'] = list(freq_range) sc2 = cube.subcube_from_regions([regpix]) scsum2 = sc2.sum() regpix.meta['restfreq'] = rf_cube vel_range_gamma = freq_range.to("km/s", equivalencies=doppler_gamma(rf_cube)) regpix.meta['range'] = list(vel_range_gamma) regpix.meta['veltype'] = 'GAMMA' sc3 = cube.subcube_from_regions([regpix]) scsum3 = sc3.sum() vel_range_beta = freq_range.to("km/s", equivalencies=doppler_beta(rf_cube)) regpix.meta['range'] = list(vel_range_beta) regpix.meta['veltype'] = 'BETA' sc4 = cube.subcube_from_regions([regpix]) scsum4 = sc4.sum() vel_range_z = freq_range.to("km/s", equivalencies=doppler_z(rf_cube)) regpix.meta['range'] = list(vel_range_z) regpix.meta['veltype'] = 'Z' sc5 = cube.subcube_from_regions([regpix]) scsum5 = sc5.sum() dsum = data[1:-1, 1, :].sum() assert_allclose(scsum1, dsum) # Proves that the vel/freq conversion works assert_allclose(scsum1, scsum2) assert_allclose(scsum2, scsum3) assert_allclose(scsum3, scsum4) assert_allclose(scsum4, scsum5)
def test_regions_spectral(): cube, data = cube_and_raw('adv.fits') rf_cube = get_rest_value_from_wcs(cube.wcs).to("GHz", equivalencies=u.spectral()) # content of image.reg regpix = regions.RectanglePixelRegion(regions.PixCoord(0.5, 1), width=4, height=2) # Velocity range in doppler_optical same as that of the cube. vel_range_optical = u.Quantity([-318 * u.km/u.s, -320 * u.km/u.s]) regpix.meta['range'] = list(vel_range_optical) sc1 = cube.subcube_from_regions([regpix]) scsum1 = sc1.sum() freq_range = vel_range_optical.to("GHz", equivalencies=u.doppler_optical(rf_cube)) regpix.meta['range'] = list(freq_range) sc2 = cube.subcube_from_regions([regpix]) scsum2 = sc2.sum() regpix.meta['restfreq'] = rf_cube vel_range_gamma = freq_range.to("km/s", equivalencies=doppler_gamma(rf_cube)) regpix.meta['range'] = list(vel_range_gamma) regpix.meta['veltype'] = 'GAMMA' sc3 = cube.subcube_from_regions([regpix]) scsum3 = sc3.sum() vel_range_beta = freq_range.to("km/s", equivalencies=doppler_beta(rf_cube)) regpix.meta['range'] = list(vel_range_beta) regpix.meta['veltype'] = 'BETA' sc4 = cube.subcube_from_regions([regpix]) scsum4 = sc4.sum() vel_range_z = freq_range.to("km/s", equivalencies=doppler_z(rf_cube)) regpix.meta['range'] = list(vel_range_z) regpix.meta['veltype'] = 'Z' sc5 = cube.subcube_from_regions([regpix]) scsum5 = sc5.sum() dsum = data[1:-1, 1, :].sum() assert_allclose(scsum1, dsum) # Proves that the vel/freq conversion works assert_allclose(scsum1, scsum2) assert_allclose(scsum2, scsum3) assert_allclose(scsum3, scsum4) assert_allclose(scsum4, scsum5)
def test_equivalency_context(): with u.set_enabled_equivalencies(u.dimensionless_angles()): phase = u.Quantity(1., u.cycle) assert_allclose(np.exp(1j*phase), 1.) Omega = u.cycle / (1.*u.minute) assert_allclose(np.exp(1j*Omega*60.*u.second), 1.) # ensure we can turn off equivalencies even within the scope with pytest.raises(u.UnitsError): phase.to(1, equivalencies=None) # test the manager also works in the Quantity constructor. q1 = u.Quantity(phase, u.dimensionless_unscaled) assert_allclose(q1.value, u.cycle.to(u.radian)) # and also if we use a class that happens to have a unit attribute. class MyQuantityLookalike(np.ndarray): pass mylookalike = np.array(1.).view(MyQuantityLookalike) mylookalike.unit = 'cycle' # test the manager also works in the Quantity constructor. q2 = u.Quantity(mylookalike, u.dimensionless_unscaled) assert_allclose(q2.value, u.cycle.to(u.radian)) with u.set_enabled_equivalencies(u.spectral()): u.GHz.to(u.cm) eq_on = u.GHz.find_equivalent_units() with pytest.raises(u.UnitsError): u.GHz.to(u.cm, equivalencies=None) # without equivalencies, we should find a smaller (sub)set eq_off = u.GHz.find_equivalent_units() assert all(eq in set(eq_on) for eq in eq_off) assert set(eq_off) < set(eq_on) # Check the equivalency manager also works in ufunc evaluations, # not just using (wrong) scaling. [#2496] l2v = u.doppler_optical(6000 * u.angstrom) l1 = 6010 * u.angstrom assert l1.to(u.km/u.s, equivalencies=l2v) > 100. * u.km / u.s with u.set_enabled_equivalencies(l2v): assert l1 > 100. * u.km / u.s assert abs((l1 - 500. * u.km / u.s).to(u.angstrom)) < 1. * u.km/u.s
def test_with_spectral_unit(self, name, masktype, unit): cube, data = cube_and_raw(name + '.fits') cube_freq = cube.with_spectral_unit(unit) if masktype == BooleanArrayMask: # don't use data here: # data haven't necessarily been rearranged to the correct shape by # cube_utils.orient mask = BooleanArrayMask(cube.filled_data[:].value > 0, wcs=cube._wcs) elif masktype == LazyMask: mask = LazyMask(lambda x: x > 0, cube=cube) elif masktype == FunctionMask: mask = FunctionMask(lambda x: x > 0) elif masktype == CompositeMask: mask1 = FunctionMask(lambda x: x > 0) mask2 = LazyMask(lambda x: x > 0, cube) mask = CompositeMask(mask1, mask2) cube2 = cube.with_mask(mask) cube_masked_freq = cube2.with_spectral_unit(unit) assert cube_freq._wcs.wcs.ctype[cube_freq._wcs.wcs.spec] == 'FREQ-W2F' assert cube_masked_freq._wcs.wcs.ctype[ cube_masked_freq._wcs.wcs.spec] == 'FREQ-W2F' assert cube_masked_freq._mask._wcs.wcs.ctype[ cube_masked_freq._mask._wcs.wcs.spec] == 'FREQ-W2F' # values taken from header rest = 1.42040571841E+09 * u.Hz crval = -3.21214698632E+05 * u.m / u.s outcv = crval.to(u.m, u.doppler_optical(rest)).to(u.Hz, u.spectral()) assert_allclose(cube_freq._wcs.wcs.crval[cube_freq._wcs.wcs.spec], outcv.to(u.Hz).value) assert_allclose( cube_masked_freq._wcs.wcs.crval[cube_masked_freq._wcs.wcs.spec], outcv.to(u.Hz).value) assert_allclose( cube_masked_freq._mask._wcs.wcs.crval[ cube_masked_freq._mask._wcs.wcs.spec], outcv.to(u.Hz).value)
def test_no_sigma(): """ test return sigma """ survey = SkySurvey() import astropy.units as u survey["INTEN"] = survey["INTEN"].data * u.R ha_wave = 656.3 * u.nm optical_ha_equiv = u.doppler_optical(ha_wave) dv = (survey["VELOCITY"][0][1] - survey["VELOCITY"][0][0]) * u.km/u.s dlambda = (dv).to(u.AA, equivalencies=optical_ha_equiv) - \ (0 * u.km/u.s).to(u.AA, equivalencies=optical_ha_equiv) survey["BKG"] = np.abs(np.random.randn(len(survey["INTEN"]))) * 100. survey["BKGSD"] = survey["BKG"] * .1 ew = survey.get_equivalent_width(return_sigma = False) ew2, ew_error2 = survey.get_equivalent_width() assert np.allclose(ew.value, ew2.value)
def __init__(self, mode='relativistic'): # HI restframe self.nu0 = 1420.4058 self.nu0_u = self.nu0 * u.MHz # full mode for Minkowski space time if mode.lower() == 'relativistic': self.v_frame = u.doppler_relativistic(self.nu0_u) # radio definition elif mode.lower() == 'radio': self.v_frame = u.doppler_radio(self.nu0_u) # velo = c * (1. - nu/nu0) # optical definition elif mode.lower() == 'optical': self.v_frame = u.doppler_optical(self.nu0_u) self.mode = mode return None
def test_convert_to_unit(run_with_assert=False): for from_unit, to_unit, _, __ in u.doppler_optical(1 * u.Hz): sp = SpectroscopicAxis( np.linspace(-100, 100, 100), unit=from_unit, refX=23.2 * u.Hz, velocity_convention="optical" ) sp.convert_to_unit(to_unit) if run_with_assert: assert sp.unit == to_unit for from_unit, to_unit, _, __ in u.doppler_radio(1 * u.eV): sp = SpectroscopicAxis( np.linspace(-100, 100, 100), unit=from_unit, refX=23.2 * u.Hz, velocity_convention="optical" ) sp.convert_to_unit(to_unit) if run_with_assert: assert sp.unit == to_unit for from_unit, to_unit, _, __ in u.doppler_relativistic(1 * u.Angstrom): sp = SpectroscopicAxis( np.linspace(-100, 100, 100), unit=from_unit, refX=23.2 * u.Hz, velocity_convention="relativistic" ) sp.convert_to_unit(to_unit) if run_with_assert: assert sp.unit == to_unit
def slice_rv(self, bounds, rest): '''Return a portion of the spectrum between given radial velocity values Parameters ---------- bounds : list of two quantities [lower bound, upper bound] in radial velocity rest : :class:`~astropy.quantity.Quantity` Rest wavelength/frequency of spectral feature Returns ------- spec : :class:`~spectrum.Spectrum` spectrum that is limited to the range from bound[0] to bound[1] See also -------- slice_disp ''' equil = u.spectral() equil.extend(u.doppler_optical(rest)) return self._slice_disp(bounds, equivalencies=equil)
def test_intensity_error_with_intensity(): """ test intensity error not provided but intensity provided """ survey = SkySurvey() import astropy.units as u survey["INTEN"] = survey["INTEN"].data * u.R ha_wave = 656.3 * u.nm optical_ha_equiv = u.doppler_optical(ha_wave) dv = (survey["VELOCITY"][0][1] - survey["VELOCITY"][0][0]) * u.km/u.s dlambda = (dv).to(u.AA, equivalencies=optical_ha_equiv) - \ (0 * u.km/u.s).to(u.AA, equivalencies=optical_ha_equiv) survey["BKG"] = np.abs(np.random.randn(len(survey["INTEN"]))) * 100. survey["BKGSD"] = survey["BKG"] * .1 try: survey.get_equivalent_width(intensity = survey["INTEN"]) except TypeError: assert True else: assert False
def test_intensity_error(): """ test intensity error providing """ survey = SkySurvey() import astropy.units as u survey["INTEN"] = survey["INTEN"].data * u.R ha_wave = 656.3 * u.nm optical_ha_equiv = u.doppler_optical(ha_wave) dv = (survey["VELOCITY"][0][1] - survey["VELOCITY"][0][0]) * u.km/u.s dlambda = (dv).to(u.AA, equivalencies=optical_ha_equiv) - \ (0 * u.km/u.s).to(u.AA, equivalencies=optical_ha_equiv) survey["BKG"] = np.abs(np.random.randn(len(survey["INTEN"]))) * 100. survey["BKGSD"] = survey["BKG"] * .1 import astropy.units as u error = survey["ERROR"].data * u.R ew, ew_error = survey.get_equivalent_width(intensity_error = error) ew2, ew_error2 = survey.get_equivalent_width() assert np.allclose(ew.value, ew2.value)
def test_with_spectral_unit(self, name, masktype, unit): cube, data = cube_and_raw(name + '.fits') cube_freq = cube.with_spectral_unit(unit) if masktype == BooleanArrayMask: # don't use data here: # data haven't necessarily been rearranged to the correct shape by # cube_utils.orient mask = BooleanArrayMask(cube.filled_data[:].value>0, wcs=cube._wcs) elif masktype == LazyMask: mask = LazyMask(lambda x: x>0, cube=cube) elif masktype == FunctionMask: mask = FunctionMask(lambda x: x>0) elif masktype == CompositeMask: mask1 = FunctionMask(lambda x: x>0) mask2 = LazyMask(lambda x: x>0, cube) mask = CompositeMask(mask1, mask2) cube2 = cube.with_mask(mask) cube_masked_freq = cube2.with_spectral_unit(unit) assert cube_freq._wcs.wcs.ctype[cube_freq._wcs.wcs.spec] == 'FREQ-W2F' assert cube_masked_freq._wcs.wcs.ctype[cube_masked_freq._wcs.wcs.spec] == 'FREQ-W2F' assert cube_masked_freq._mask._wcs.wcs.ctype[cube_masked_freq._mask._wcs.wcs.spec] == 'FREQ-W2F' # values taken from header rest = 1.42040571841E+09*u.Hz crval = -3.21214698632E+05*u.m/u.s outcv = crval.to(u.m, u.doppler_optical(rest)).to(u.Hz, u.spectral()) assert_allclose(cube_freq._wcs.wcs.crval[cube_freq._wcs.wcs.spec], outcv.to(u.Hz).value) assert_allclose(cube_masked_freq._wcs.wcs.crval[cube_masked_freq._wcs.wcs.spec], outcv.to(u.Hz).value) assert_allclose(cube_masked_freq._mask._wcs.wcs.crval[cube_masked_freq._mask._wcs.wcs.spec], outcv.to(u.Hz).value)
def test_ccf(seed, snr): # Make test reproducible: np.random.seed(seed) # Generate a wavelength grid: wl = np.arange(6800, 6900, 0.1) # Generate a pseudo-random absorption pattern absorption_pattern = gaussian_filter1d(0.1 * np.random.randn(len(wl)), 5) absorption_pattern = absorption_pattern - absorption_pattern.min() # Roll the absorption pattern offset by 3 indices index_offset = 3 fl = np.ones_like(wl) - np.roll(absorption_pattern, index_offset) spectrum = Spectrum(wl, fl) emission = absorption_pattern / np.trapz(absorption_pattern, wl) template = Template(wl, emission) ccf = cross_corr(spectrum, template, start_lam=-10, end_lam=10, n_steps=100) # Check that S/N is approximately correct: assert abs(ccf.signal_to_noise - snr) < 1 # Check that the estimated radial velocity is equivalent to index offset ref_wl = 6800 * u.Angstrom measured_velocity_offset = ( ccf.rv.to(u.Angstrom, u.doppler_optical(ref_wl)) - ref_wl) / ( (wl[1] - wl[0]) * u.Angstrom) assert_quantity_allclose(measured_velocity_offset, index_offset, atol=1e-2)
def frequencies(self): if self._frequencies is None: specax = self.wcs.wcs.spec channels = np.arange(self.data.shape[::-1][specax]) spec = self.spec_wcs.wcs_pix2world(channels, 0)[0] specc = spec * self.axis_units[specax] # Convert radio or optical velocities to frequencies if 'VRAD' in self.wcs.wcs.ctype[specax]: eq = u.doppler_radio(self.rest_frequency) elif 'VOPT' in self.wcs.wcs.ctype[specax]: eq = u.doppler_optical(self.rest_frequency) elif 'FREQ' in self.wcs.wcs.ctype[specax]: eq = [] else: raise AttributeError( 'Unsupported spectral type {:s} in header.'.format(self.wcs.wcs.ctype[specax])) self._frequencies = specc.to(u.Hz, eq) return self._frequencies
def test_byhand_vopt(): # VOPT: case "Z" CRVAL3F = 1.37847121643E+09 CDELT3F = 9.764775E+04 CUNIT3F = 'Hz' RESTWAVZ= 0.211061139 #CTYPE3Z = 'VOPT-F2W' # This comes from Greisen 2006, but appears to be wrong: CRVAL3Z = 9.120000E+06 CRVAL3Z = 9.120002206E+06 CDELT3Z = -2.1882651E+04 CUNIT3Z = 'm/s' crvalf = CRVAL3F * u.Unit(CUNIT3F) crvalv = CRVAL3Z * u.Unit(CUNIT3Z) restwav = RESTWAVZ * u.m cdeltf = CDELT3F * u.Unit(CUNIT3F) cdeltv = CDELT3Z * u.Unit(CUNIT3Z) # Forward: freq -> vopt # crval: (<Quantity 1378471216.43 Hz>, <Quantity 1378471216.43 Hz>, <Quantity 0.2174818410618759 m>, <Quantity 9120002.205689976 m / s>) # cdelt: (<Quantity 97647.75 Hz>, <Quantity 97647.75 Hz>, <Quantity -1.540591649098696e-05 m>, <Quantity -21882.652554887027 m / s>) #crvalv_computed = crvalf.to(CUNIT3R, u.doppler_radio(restwav)) crvalw_computed = crvalf.to(u.m, u.spectral()) crvalw_computed32 = crvalf.astype('float32').to(u.m, u.spectral()) cdeltw_computed = -(cdeltf / crvalf**2)*constants.c cdeltw_computed_byfunction = cdelt_derivative(crvalf, cdeltf, intype='frequency', outtype='length', rest=None) # this should be EXACT assert cdeltw_computed == cdeltw_computed_byfunction crvalv_computed = crvalw_computed.to(CUNIT3Z, u.doppler_optical(restwav)) crvalv_computed32 = crvalw_computed32.astype('float32').to(CUNIT3Z, u.doppler_optical(restwav)) #cdeltv_computed = (cdeltw_computed * # 4*constants.c*crvalw_computed*restwav**2 / # (restwav**2+crvalw_computed**2)**2) cdeltv_computed = (cdeltw_computed / restwav)*constants.c cdeltv_computed_byfunction = cdelt_derivative(crvalw_computed, cdeltw_computed, intype='length', outtype='speed', rest=restwav, linear=True) # Disagreement is 2.5e-7: good, but not really great... #assert np.abs((crvalv_computed-crvalv)/crvalv) < 1e-6 assert_allclose(crvalv_computed, crvalv, rtol=1.e-2) assert_allclose(cdeltv_computed, cdeltv, rtol=1.e-2) # Round=trip test: # from velo_opt -> freq # (<Quantity 9120002.205689976 m / s>, <Quantity 0.2174818410618759 m>, <Quantity 1378471216.43 Hz>, <Quantity 1378471216.43 Hz>) # (<Quantity -21882.652554887027 m / s>, <Quantity -1.540591649098696e-05 m>, <Quantity 97647.75 Hz>, <Quantity 97647.75 Hz>) crvalw_computed = crvalv_computed.to(u.m, u.doppler_optical(restwav)) cdeltw_computed = (cdeltv_computed/constants.c) * restwav cdeltw_computed_byfunction = cdelt_derivative(crvalv_computed, cdeltv_computed, intype='speed', outtype='length', rest=restwav, linear=True) assert cdeltw_computed == cdeltw_computed_byfunction crvalf_computed = crvalw_computed.to(CUNIT3F, u.spectral()) cdeltf_computed = -cdeltw_computed * constants.c / crvalw_computed**2 assert_allclose(crvalf_computed, crvalf, rtol=1.e-3) assert_allclose(cdeltf_computed, cdeltf, rtol=1.e-3) cdeltf_computed_byfunction = cdelt_derivative(crvalw_computed, cdeltw_computed, intype='length', outtype='frequency', rest=None) assert cdeltf_computed == cdeltf_computed_byfunction
def wave2doppler(w, w0): import astropy.units as u w0_equiv = u.doppler_optical(w0) w_equiv = w.to(u.km / u.s, equivalencies=w0_equiv) return w_equiv
def __init__(self, filename, config, lambda0=0., verbose=False): '''Read datacube and WCS''' self.wide_figsize = matplotlib.rcParams['figure.figsize'].copy() self.wide_figsize[0] = self.wide_figsize[1]*16./9. self.verbose = verbose # separate our output from the bullshit printed by import print('============================================') self.filename = filename print("Source file ", filename) self.src_fits = fits.open(filename) self.src_cube = self.src_fits[0].data self.src_header = self.src_fits[0].header naxis3, naxis2, naxis1 = self.src_cube.shape print(naxis1, "x", naxis2, "pixels,", naxis3, "spectral channels") self.plotpix = np.asarray((naxis2//2, naxis1//2)) # Parse the WCS keywords in primary HDU #del self.src_header['VELREF'] # no AIPS stuff, please self.wcs = wcs.WCS(self.src_header) pixscale1, pixscale2 = wcs.utils.proj_plane_pixel_scales(self.wcs)[0:2] pixscale1 = (pixscale1 * self.wcs.wcs.cunit[0]).to(u.mas) pixscale2 = (pixscale2 * self.wcs.wcs.cunit[1]).to(u.mas) #print("image pixscale from WCS:", pixscale1, pixscale2, "/pixel") self.src_pixscale = np.asarray((pixscale1.value, pixscale2.value))*u.mas if self.verbose: print("image pixscale:", self.src_pixscale, "per pixel") # Check flux units of data cube try: bunit = self.src_header['BUNIT'] print("BUNIT:", bunit) if bunit.lower() == 'jy/pixel': #plt.semilogy(self.src_cube[:,111,100]) #print(np.max(self.src_cube[:,111,100])) pixarea = (wcs.utils.proj_plane_pixel_area(self.wcs) * self.wcs.wcs.cunit[0] * self.wcs.wcs.cunit[1]).to(u.arcsec*u.arcsec) if self.verbose: print("Pixel area is", pixarea) self.src_cube /= pixarea.value #plt.semilogy(self.src_cube[:,111,100]) #plt.show() #print(np.max(self.src_cube[:,111,100])) except KeyError: print("Keyword BUNIT not found. Assuming the data is in units of Jy/arcsec2") # make a Nlambda x 3 array crd = np.zeros((naxis3, 3)) crd[:, 2] = np.arange(naxis3) # Convert pixel coordinates to world coordinates # Second argument is "origin" -- in this case 0-based coordinates world_coo = self.wcs.wcs_pix2world(crd, 0)[:, 2] * self.wcs.wcs.cunit[2] if self.verbose: print("CTYPES:", self.wcs.wcs.ctype) print("CUNITS:", self.wcs.wcs.cunit) if self.wcs.wcs.ctype[2] == 'VRAD' or self.wcs.wcs.ctype[2] == 'VOPT': print("WARNING: Your Fits header specifies the spectral axis as", self.wcs.wcs.ctype[2]) print(" We treat it as apparent radial velocity (VELO)") self.wcs.wcs.ctype[2] = 'VELO' if self.wcs.wcs.ctype[2] == 'VELO': #print("Velocities:",world_coo) # this should be in the header, shouldn't it? if lambda0 > 0.: restcoo = lambda0 * u.um # unit of lambda0 must be micron elif self.wcs.wcs.restwav > 0: restcoo = (self.wcs.wcs.restwav * u.m).to(u.um) elif self.wcs.wcs.restfrq > 0: restcoo = (self.wcs.wcs.restfrq * u.Hz).to(u.um, equivalencies=u.spectral()) else: raise RuntimeError("Cannot determine rest wavelength of velocity scale.") wavelen = restcoo * (1. + world_coo / const.c) #print("Wavelengths:",wavelen) elif self.wcs.wcs.ctype[2] == 'WAVE': # TODO: test me! wavelen = world_coo.to(u.um) restcoo = self.wcs.wcs.restwav * self.wcs.wcs.cunit[2] elif self.wcs.wcs.ctype[2] == 'FREQ': wavelen = world_coo.to(u.um, equivalencies=u.spectral()) restcoo = self.wcs.wcs.restfrq * self.wcs.wcs.cunit[2] else: print("spectral axis is '", self.wcs.wcs.ctype[2], "'") raise NotImplementedError('spectral axis must have type VELO, WAVE, or FREQ') self.wavelen = wavelen.value # should we store it with units? self.restcoo = restcoo # RESTFRQ or RESTWAV with units if self.verbose: print("Wavelengths:", wavelen[0:3], "...", wavelen[-1]) print("restfrq:", self.wcs.wcs.restfrq) print("restwav:", self.wcs.wcs.restwav) print("restcoo:", self.restcoo) #print("Rest wavelen:", restcoo.to(u.um, equivalencies=u.spectral())) in_velos = wavelen.to(u.m/u.s, equivalencies=u.doppler_optical(restcoo)) step = 1.5 * u.km/u.s new3 = int((in_velos[-1] - in_velos[0]) / step)+1 meanv = (in_velos[0] + in_velos[-1]) / 2. self.det_velocities = np.arange((meanv-step*(new3-1)/2.).to(u.m/u.s).value, (meanv+step*((new3-1)/2.+1)).to(u.m/u.s).value, step.to(u.m/u.s).value) if self.verbose: print("Source velocities (WCS):", in_velos[0], "...", in_velos[-1]) #print(in_velos) print("new naxis3:", new3) #print("middle v:", meanv) print("Detector velocities:", self.det_velocities.shape, self.det_velocities[0], "...", self.det_velocities[-1]) # initialize the target cube, in case someone does not call transmission_emission() self.target_cube = self.src_cube self.target_hdr = self.src_header.copy() self.target_hdr['BUNIT'] = 'Jy/arcsec2' self.background = None self.transmission = 1. self.emission = None # will be computed later # initialize SimCADO to set searchpaths # print("Reading config", config) self.cmds = sm.UserCommands(config) self.det_pixscale = self.cmds["SIM_DETECTOR_PIX_SCALE"] * 1000. # in mas/pixel if self.verbose: print("Detector pixel scale ", self.det_pixscale, " mas/pixel") print("Filter = ", self.cmds["INST_FILTER_TC"]) # should be open filter if np.max(self.src_pixscale.value) > self.det_pixscale: print("+------------------------------------------------------------------------------+") print("| WARNING: MAX. PIXEL SCALE OF INPUT CUBE IS LARGER THAN DETECTOR PIXEL SCALE! |") print("|", np.max(self.src_pixscale), "/pixel >", self.det_pixscale, " mas/pixel") print("+------------------------------------------------------------------------------+")
def wave2doppler(w, w0): w0_equiv = u.doppler_optical(w0) w_equiv = w.to(u.km / u.s, equivalencies=w0_equiv) return w_equiv
def optical_velocity(self, unit): rest_freq = self._rest_wave.to(u.Hz, equivalencies=u.spectral()) optical_equiv = u.doppler_optical(rest_freq) return self._sp_coord.to(unit, equivalencies=u.optical_equiv())
def __init__(self): self.x = units.SpectroscopicAxis(np.arange(5), unit=u.angstrom, velocity_convention='optical', equivalencies=u.doppler_optical(3*u.AA)) self.length = units.SpectroscopicAxis(np.arange(5), unit=u.nm) self.frequency = units.SpectroscopicAxis(np.arange(5), unit=u.GHz) self.speed = units.SpectroscopicAxis(np.arange(5), unit=u.km/u.s)
def test_equivalencies_1(): x = units.SpectroscopicAxis(np.arange(5), unit=u.angstrom, velocity_convention='optical', equivalencies=u.doppler_optical(3*u.AA)) assert x.equivalencies is not None # == u.doppler_optical(3*u.AA)
def test_convert_to(unit_from, unit_to, convention, ref_unit): x = units.SpectroscopicAxis(np.arange(5), unit=u.angstrom, velocity_convention='optical', equivalencies=u.doppler_optical(3*u.AA)) x.convert_to_unit(unit_to,convention=convention)
def plot_image_and_lines(cube, wavs, xrange, yrange, Hbeta_ref=None, title='', filename=None, include_OIII=False): zpix = np.arange(0, cube.shape[0]) lambda_delta = 5 hbeta_z = np.where((np.array(wavs) > h_beta_std.value-lambda_delta)\ & (np.array(wavs) < h_beta_std.value+lambda_delta))[0] image = np.mean(cube[min(hbeta_z):max(hbeta_z) + 1, :, :], axis=0) spect = [ np.mean(cube[z, yrange[0]:yrange[1] + 1, xrange[0]:xrange[1] + 1]) for z in zpix ] i_peak = spect.index(max(spect)) background_0 = models.Polynomial1D(degree=2) H_beta_0 = models.Gaussian1D(amplitude=500, mean=4861, stddev=1., bounds={ 'mean': (4855, 4865), 'stddev': (0.1, 5) }) OIII4959_0 = models.Gaussian1D(amplitude=100, mean=4959, stddev=1., bounds={ 'mean': (4955, 4965), 'stddev': (0.1, 5) }) OIII5007_0 = models.Gaussian1D(amplitude=200, mean=5007, stddev=1., bounds={ 'mean': (5002, 5012), 'stddev': (0.1, 5) }) fitter = fitting.LevMarLSQFitter() if include_OIII is True: model0 = background_0 + H_beta_0 + OIII4959_0 + OIII5007_0 else: model0 = background_0 + H_beta_0 model0.mean_1 = wavs[i_peak] model = fitter(model0, wavs, spect) residuals = np.array(spect - model(wavs)) plt.figure(figsize=(20, 8)) plt.subplot(1, 4, 1) plt.title(title) norm = v.ImageNormalize(image, interval=v.MinMaxInterval(), stretch=v.LogStretch(1)) plt.imshow(image, origin='lower', norm=norm) region_x = [ xrange[0] - 0.5, xrange[1] + 0.5, xrange[1] + 0.5, xrange[0] - 0.5, xrange[0] - 0.5 ] region_y = [ yrange[0] - 0.5, yrange[0] - 0.5, yrange[1] + 0.5, yrange[1] + 0.5, yrange[0] - 0.5 ] plt.plot(region_x, region_y, 'r-', alpha=0.5, lw=2) plt.subplot(1, 4, 2) if Hbeta_ref is not None: Hbeta_velocity = (model.mean_1.value * u.Angstrom).to( u.km / u.s, equivalencies=u.doppler_optical(Hbeta_ref)) title = f'H-beta ({model.mean_1.value:.1f} A, v={Hbeta_velocity.value:.1f} km/s)' else: title = f'H-beta ({model.mean_1.value:.1f} A, sigma={model.stddev_1.value:.3f} A)' plt.title(title) w = [l for l in np.arange(4856, 4866, 0.05)] if Hbeta_ref is not None: vs = [(l * u.Angstrom).to( u.km / u.s, equivalencies=u.doppler_optical(Hbeta_ref)).value for l in wavs] plt.plot(vs, spect, drawstyle='steps-mid', label='data') vs = [(l * u.Angstrom).to( u.km / u.s, equivalencies=u.doppler_optical(Hbeta_ref)).value for l in w] plt.plot(vs, model(w), 'r-', alpha=0.7, label='Fit') plt.xlabel('Velocity (km/s)') plt.xlim(-200, 200) else: plt.plot(wavs, spect, drawstyle='steps-mid', label='data') plt.plot(w, model(w), 'r-', alpha=0.7, label='Fit') plt.xlabel('Wavelength (angstroms)') plt.xlim(4856, 4866) plt.grid() plt.ylabel('Flux') plt.legend(loc='best') plt.subplot(1, 4, 3) if include_OIII is True: title = f'OIII 4959 ({model.mean_2.value:.1f} A, sigma={model.stddev_2.value:.3f} A)' else: title = f'OIII 4959' plt.title(title) plt.plot(wavs, spect, drawstyle='steps-mid', label='data') w = [l for l in np.arange(4954, 4964, 0.05)] plt.plot(w, model(w), 'r-', alpha=0.7, label='Fit') plt.xlabel('Wavelength (angstroms)') plt.ylabel('Flux') plt.legend(loc='best') plt.xlim(4954, 4964) plt.subplot(1, 4, 4) if include_OIII is True: title = f'OIII 5007 ({model.mean_3.value:.1f} A, sigma={model.stddev_3.value:.3f} A)' else: title = f'OIII 5007' plt.title(title) plt.plot(wavs, spect, drawstyle='steps-mid', label='data') w = [l for l in np.arange(5002, 5012, 0.05)] plt.plot(w, model(w), 'r-', alpha=0.7, label='Fit') plt.xlabel('Wavelength (angstroms)') plt.ylabel('Flux') plt.legend(loc='best') plt.xlim(5002, 5012) if filename is not None: plt.savefig(filename, bbox_inches='tight', pad_inches=0.10) else: plt.show() return spect, model
import os import logging from datetime import datetime as dt from astropy.io import fits from astropy.wcs import WCS from astropy.modeling import models, fitting from astropy import units as u import numpy as np from matplotlib import pyplot as plt from astropy import visualization as v h_beta_std = 4861.3615 * u.Angstrom H_beta_std_velocity = u.doppler_optical(h_beta_std) ##------------------------------------------------------------------------- ## Create logger object ##------------------------------------------------------------------------- log = logging.getLogger('MyLogger') log.setLevel(logging.DEBUG) ## Set up console output LogConsoleHandler = logging.StreamHandler() LogConsoleHandler.setLevel(logging.DEBUG) LogFormat = logging.Formatter('%(asctime)s %(levelname)8s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S') LogConsoleHandler.setFormatter(LogFormat) log.addHandler(LogConsoleHandler) ##-------------------------------------------------------------------------
def optical_velocities(self): if self._optical_velocities is None: opt_eq = u.doppler_optical(self.rest_frequency) self._optical_velocities = self.frequencies.to(u.km / u.s, opt_eq) return self._optical_velocities
def test_equivalencies_3(velocity_convention): x = SpectroscopicAxis(np.arange(5), unit=u.angstrom, refX=3, refX_unit='angstrom', velocity_convention=velocity_convention) assert x.equivalencies == u.doppler_optical(3*u.AA)
def scale_to_detector(self): ''' Scale to Detector pixels (spatially and spectrally) ''' print() print("--------------------Scale to detector pixels--------------------") # First interpolate spectrally naxis3, naxis2, naxis1 = self.target_cube.shape in_velos = (self.wavelen * u.um).to(u.m/u.s, equivalencies=u.doppler_optical(self.restcoo)) #print("Original velos:", in_velos[0], "...", in_velos[-1]) out_velos = self.det_velocities #print("New velos:", out_velos.shape, out_velos[0], "...", out_velos[-1]) #print(out_velos) scaled_cube = np.empty((len(out_velos), naxis2, naxis1), self.target_cube[0, 0, 0].dtype) for i_x in range(naxis2): #print(i_x, end=' ',flush=True) for i_y in range(naxis1): intpol = interp1d(in_velos, self.target_cube[:, i_y, i_x], kind='linear', bounds_error=False, fill_value=0.) scaled_cube[:, i_y, i_x] = intpol(out_velos) #print() self.target_cube = scaled_cube self.wcs.wcs.ctype[2] = 'VELO' self.wcs.wcs.crpix[2] = 1 self.wcs.wcs.crval[2] = out_velos[0] self.wcs.wcs.cdelt[2] = out_velos[1] - out_velos[0] # should be 1.5km/s self.wcs.wcs.cunit[2] = 'm/s' # # Now interpolate spatially # naxis3, naxis2, naxis1 = self.target_cube.shape if self.verbose: print("ScaleToDetector: naxis =", naxis1, naxis2, naxis3) in_x = np.arange(naxis1) in_y = np.arange(naxis2) #print(len(in_x)) # TODO better: scale from the center of the img scale = self.src_pixscale.value / self.det_pixscale if self.verbose: print("image pixscale from WCS:", self.src_pixscale, "/pixel") print("Scale factor:", scale) self.plotpix = np.rint(self.plotpix * scale).astype(int) ## we need to scale the coord of the last pixel, not the pixel behind the end! #out_x = np.arange(round((naxis1-1)*scale[0])+1) / scale[0] #out_y = np.arange(round((naxis2-1)*scale[1])+1) / scale[1] # scale from the center of the image half1 = naxis1 // 2 half2 = naxis2 // 2 # scale the coord of the last pixel, not the pixel behind the end! out_x = np.arange(half1 - round(half1 * scale[1]) / scale[1], half1 + round((half1 - 1) * scale[1]) / scale[1] + 1, 1. / scale[1]) out_y = np.arange(half2 - round(half2 * scale[0]) / scale[0], half2 + round((half2 - 1) * scale[0]) / scale[0] + 1, 1. / scale[0]) scaled_cube = np.empty((naxis3, len(out_y), len(out_x)), self.target_cube[0, 0, 0].dtype) for i in range(naxis3): # bilinear interpol interp = RectBivariateSpline(in_x, in_y, self.target_cube[i, :, :], kx=1, ky=1) scaled_cube[i, :, :] = interp(out_x, out_y, grid=True) if self.verbose: print("ScaleToDetector: new shape =", scaled_cube.shape) self.target_cube = scaled_cube # new_CRPIX = old_CRPIX * new_size/old_size # # old_pixscale = FoV/old_size # new_pixscale = FoV/new_size # scale = old_pixscale / new_pixscale = FoV/old_size * new_size/FoV = new_size/old_size # # new_CRPIX = old_CRPIX * scale # self.wcs.wcs.crpix[0] = round(self.wcs.wcs.crpix[0] * scale[0]) # or [1]? self.wcs.wcs.crpix[1] = round(self.wcs.wcs.crpix[1] * scale[1]) self.wcs.wcs.cdelt[0] = -self.det_pixscale / 3600. / 1000. # convert mas/pix to deg/pix self.wcs.wcs.cdelt[1] = self.det_pixscale / 3600. / 1000. self.wcs.wcs.cunit[0] = 'deg' self.wcs.wcs.cunit[1] = 'deg' # overwrite old WCS, but keep rest of the header for card in self.wcs.to_header().cards: self.target_hdr[card.keyword] = (card.value, card.comment)
def setup_class(cls): cls.x = units.SpectroscopicAxis(np.arange(5), unit=u.angstrom, velocity_convention='optical', equivalencies=u.doppler_optical(3*u.AA)) cls.length = units.SpectroscopicAxis(np.arange(5), unit=u.nm) cls.frequency = units.SpectroscopicAxis(np.arange(5), unit=u.GHz) cls.speed = units.SpectroscopicAxis(np.arange(5), unit=u.km/u.s) return cls
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
from __future__ import print_function import pyspeckit from astropy import units as u sp = pyspeckit.Spectrum('G203.04+1.76_h2co.fits',wcstype='D') sp.xarr.center_frequency._unit = u.Hz sp.xarr = sp.xarr.as_unit('km/s', equivalencies=u.doppler_radio(sp.xarr.center_frequency)) sp.specfit(fittype='formaldehyde',multifit=True,usemoments=True,guesses=[-0.6,4,0.2],equivalencies=u.doppler_optical(sp.xarr.center_frequency)) sp.plotter(figure=2) sp.crop(-5*u.km/u.s,15*u.km/u.s) sp.specfit(fittype='formaldehyde',multifit=True,usemoments=True,guesses=[-0.6,4,0.2]) sp.specfit(fittype='formaldehyde',multifit=True,usemoments=True,guesses=[-0.6,4,0.2]) sp.specfit.plot_fit(show_components=True) sp.plotter.savefig("h2co_11_h2cofit.png") print("Line integral (Formaldehyde,11): ",sp.specfit.integral()) print("Direct Line integral (Formaldehyde,11): ",sp.specfit.integral(direct=True,return_error=True)) sp2 = pyspeckit.Spectrum('G203.04+1.76_h2co.fits',wcstype='V') sp2.xarr.convert_to_unit('km/s') sp2.crop(-5*u.km/u.s,15*u.km/u.s) sp2.plotter(figure=3) sp2.specfit.peakbgfit(negamp=True, vheight=False) sp2.specfit.peakbgfit(negamp=True, vheight=False) sp2.plotter.savefig("h2co_11_gaussfit.png") print("Line integral (Gaussian,11): ",sp2.specfit.integral()) print("Direct Line integral (Gaussian,11): ",sp2.specfit.integral(direct=True,return_error=True)) sp22g = pyspeckit.Spectrum('G203.04+1.76_h2co_Tastar.fits',wcstype='V') sp22g.specfit.peakbgfit(negamp=True) sp22g.xarr = sp22g.xarr.as_unit('km/s') sp22g.plotter(figure=5)