def test_mag_ab_redshift_dependence(): from astropy import units from skypy.galaxy.spectrum import mag_ab # make a wide tophat bandpass bp_lam = np.logspace(-10, 10, 3) * units.AA bp_tx = np.ones(3) * units.dimensionless_unscaled bp = specutils.Spectrum1D(spectral_axis=bp_lam, flux=bp_tx) # create a narrow gaussian source lam = np.logspace(0, 3, 1000) * units.AA flam = np.exp(-((lam - 100 * units.AA) / (10 * units.AA))**2) * units.Unit('erg s-1 cm-2 AA-1') spec = specutils.Spectrum1D(spectral_axis=lam, flux=flam) # array of redshifts z = np.linspace(0, 1, 11) # compute the AB magnitude at different redshifts m = mag_ab(spec, bp, z) # compare with expected redshift dependence np.testing.assert_allclose(m, m[0] - 2.5 * np.log10(1 + z))
def test_mag_ab_multi(): from astropy import units from skypy.galaxy.spectrum import mag_ab # 5 redshifts z = np.linspace(0, 1, 5) # 2 Gaussian bandpasses bp_lam = np.logspace(0, 4, 1000) * units.AA bp_mean = np.array([[1000], [2000]]) * units.AA bp_width = np.array([[100], [10]]) * units.AA bp_tx = np.exp(-( (bp_lam - bp_mean) / bp_width)**2) * units.dimensionless_unscaled bp = specutils.Spectrum1D(spectral_axis=bp_lam, flux=bp_tx) # 3 Flat Spectra lam = np.logspace(0, 4, 1000) * units.AA A = np.array([[2], [3], [4]]) flam = A * 0.10884806248538730623 * units.Unit('erg s-1 cm-2 AA') / lam**2 spec = specutils.Spectrum1D(spectral_axis=lam, flux=flam) # Compare calculated magnitudes with truth magnitudes = mag_ab(spec, bp, z) truth = -2.5 * np.log10(A * (1 + z)).T[:, np.newaxis, :] assert magnitudes.shape == (5, 2, 3) np.testing.assert_allclose(*np.broadcast_arrays(magnitudes, truth))
def test_template_spectra(): from astropy import units from skypy.galaxy.spectrum import mag_ab, magnitudes_from_templates from astropy.cosmology import Planck15 # 3 Flat Templates lam = np.logspace(0, 4, 1000) * units.AA A = np.array([[2], [3], [4]]) flam = A * 0.10884806248538730623 * units.Unit('erg s-1 cm-2 AA') / lam**2 spec = specutils.Spectrum1D(spectral_axis=lam, flux=flam) # Gaussian bandpass bp_lam = np.logspace(0, 4, 1000) * units.AA bp_tx = np.exp(-((bp_lam - 1000 * units.AA) / (100 * units.AA))**2) * units.dimensionless_unscaled bp = specutils.Spectrum1D(spectral_axis=bp_lam, flux=bp_tx) # Each test galaxy is exactly one of the templates coefficients = np.diag(np.ones(3)) mt = magnitudes_from_templates(coefficients, spec, bp) m = mag_ab(spec, bp) np.testing.assert_allclose(mt, m) # Test distance modulus redshift = np.array([0, 1, 2]) dm = Planck15.distmod(redshift).value mt = magnitudes_from_templates(coefficients, spec, bp, distance_modulus=dm) np.testing.assert_allclose(mt, m + dm) # Test stellar mass sm = np.array([1, 2, 3]) mt = magnitudes_from_templates(coefficients, spec, bp, stellar_mass=sm) np.testing.assert_allclose(mt, m - 2.5 * np.log10(sm)) # Redshift interpolation test; linear interpolation sufficient over a small # redshift range at low relative tolerance z = np.linspace(0, 0.1, 3) m_true = magnitudes_from_templates(coefficients, spec, bp, redshift=z, resolution=4) m_interp = magnitudes_from_templates(coefficients, spec, bp, redshift=z, resolution=2) np.testing.assert_allclose(m_true, m_interp, rtol=1e-2) with pytest.raises(AssertionError): np.testing.assert_allclose(m_true, m_interp, rtol=1e-5)
def skypy_data_loader(module, name, *tags): '''load data from the skypy data package''' # result is spectrum or list of spectra spectra = None # load each tag separately for tag in tags: # get resource filename from module, name, and tag filename = resource_filename(f'skypy-data.{module}', f'{name}_{tag}.ecsv') # load the data file data = astropy.table.Table.read(filename, format='ascii.ecsv') # get the spectral axis spectral_axis = data['spectral_axis'].quantity # load all templates flux_unit = data['flux_0'].unit fluxes = [] while 'flux_%d' % len(fluxes) in data.colnames: fluxes.append(data['flux_%d' % len(fluxes)].quantity.to_value(flux_unit)) fluxes = np.squeeze(fluxes) * flux_unit # construct the Spectrum1D spectrum = specutils.Spectrum1D(spectral_axis=spectral_axis, flux=fluxes) # combine with existing spectra = combine_spectra(spectra, spectrum) return spectra
def snth_spectrum_1000(NGC4945_continuum_rest_frame): real_spectrum = NGC4945_continuum_rest_frame freq_axis = real_spectrum.frequency_axis sinthetic_model = nd.normalized_blackbody(1000) sinthetic_flux = sinthetic_model(freq_axis.value) mu, sigma = 0, 0.1 nd_random = np.random.RandomState(50) gaussian_noise = nd_random.normal(mu, sigma, len(freq_axis)) noisy_model = sinthetic_flux * (1 * u.adu + gaussian_noise * u.adu) dispersion = 3.51714285129581 first_wave = 18940.578099674 dispersion_type = "LINEAR " spectrum_length = len(real_spectrum.flux) spectral_axis = (first_wave + dispersion * np.arange(0, spectrum_length)) * u.AA spec1d = su.Spectrum1D(flux=noisy_model, spectral_axis=spectral_axis) frequency_axis = spec1d.spectral_axis.to(u.Hz) snth_spectrum = NirdustSpectrum( header=None, z=0, spectrum_length=spectrum_length, dispersion_key=None, first_wavelength=None, dispersion_type=dispersion_type, spec1d=spec1d, frequency_axis=frequency_axis, ) return snth_spectrum
def decam_loader(*bands): '''load DECam bandpass filters''' # download DECam filter data filename = download_file( 'http://www.ctio.noao.edu/noao/sites/default/files/DECam/STD_BANDPASSES_DR1.fits' ) # load the data file data = astropy.table.Table.read(filename, format='fits') # set units data['LAMBDA'].unit = units.angstrom # get the spectral axis spectral_axis = data['LAMBDA'].quantity # load requested bands throughput = [] for band in bands: throughput.append(data[band]) throughput = np.squeeze(throughput) * units.dimensionless_unscaled # return the bandpasses as Spectrum1D return specutils.Spectrum1D(spectral_axis=spectral_axis, flux=throughput)
def test_fit_blackbody(NGC4945_continuum_rest_frame): real_spectrum = NGC4945_continuum_rest_frame freq_axis = real_spectrum.frequency_axis sinthetic_model = BlackBody(1000 * u.K) sinthetic_flux = sinthetic_model(freq_axis) dispersion = 3.51714285129581 first_wave = 18940.578099674 dispersion_type = "LINEAR " spectrum_length = len(real_spectrum.flux) spectral_axis = (first_wave + dispersion * np.arange(0, spectrum_length)) * u.AA spec1d = su.Spectrum1D(flux=sinthetic_flux, spectral_axis=spectral_axis) frequency_axis = spec1d.spectral_axis.to(u.Hz) snth_blackbody = NirdustSpectrum( header=None, z=0, spectrum_length=spectrum_length, dispersion_key=None, first_wavelength=None, dispersion_type=dispersion_type, spec1d=spec1d, frequency_axis=frequency_axis, ) snth_bb_temp = (snth_blackbody.normalize().convert_to_frequency(). fit_blackbody(1200).temperature) np.testing.assert_almost_equal(snth_bb_temp.value, 1000, decimal=7)
def test_mag_ab_standard_source(): from astropy import units from skypy.galaxy.spectrum import mag_ab # create a bandpass bp_lam = np.logspace(0, 4, 1000) * units.AA bp_tx = np.exp(-((bp_lam - 1000 * units.AA) / (100 * units.AA))**2) * units.dimensionless_unscaled bp = specutils.Spectrum1D(spectral_axis=bp_lam, flux=bp_tx) # test that the AB standard source has zero magnitude lam = np.logspace(0, 4, 1000) * units.AA flam = 0.10884806248538730623 * units.Unit('erg s-1 cm-2 AA') / lam**2 spec = specutils.Spectrum1D(spectral_axis=lam, flux=flam) m = mag_ab(spec, bp) assert np.isclose(m, 0)
def test_from_spectrum1d_Empirical1D_bandpass_masked(self): import specutils lamb = [1000, 5000, 10000] * u.AA thru = [0, 1, -1] * units.THROUGHPUT mask = np.array([False, False, True]) spec = specutils.Spectrum1D(spectral_axis=lamb, flux=thru, mask=mask) bp = SpectralElement.from_spectrum1d(spec, keep_neg=False) w = bp.waveset assert isinstance(bp.model, Empirical1D) assert_quantity_allclose(w, [1000, 5000] * u.AA) assert_quantity_allclose(bp(w), [0, 1])
def test_from_spectrum1d_Empirical1D_source_masked(self): import specutils lamb = [1000, 5000, 10000] * u.AA flux = [0, -0.5e-17, 5.6e-17] * units.FLAM mask = np.array([False, True, False]) spec = specutils.Spectrum1D(spectral_axis=lamb, flux=flux, mask=mask) sp = SourceSpectrum.from_spectrum1d(spec, keep_neg=False) w = sp.waveset y = sp(w, flux_unit=units.FLAM) assert_quantity_allclose(w, [1000, 10000] * u.AA) assert_quantity_allclose(y, [0, 5.6e-17] * units.FLAM)
def normalize(self): """Normalize the spectrum to the unity using the mean value. Returns ------- out: NirsdustSpectrum object New instance of the NirdustSpectrun class with the flux normalized to unity. """ normalized_flux = self.spec1d.flux / np.mean(self.spec1d.flux) new_spec1d = su.Spectrum1D(normalized_flux, self.spec1d.spectral_axis) kwargs = attr.asdict(self) kwargs.update(spec1d=new_spec1d) return NirdustSpectrum(**kwargs)
def test_from_spectrum1d_Empirical1D_bandpass(self): import specutils lamb = [1000, 5000, 10000] * u.AA thru = [0, 1, -1] * units.THROUGHPUT spec = specutils.Spectrum1D(spectral_axis=lamb, flux=thru) with pytest.warns(AstropyUserWarning, match=r'contained negative flux or throughput'): bp = SpectralElement.from_spectrum1d(spec, keep_neg=False) w = bp.waveset assert isinstance(bp.model, Empirical1D) assert_quantity_allclose(w, lamb) assert_quantity_allclose(bp(w), [0, 1, 0])
def test_combine_spectra(): from skypy.galaxy._spectrum_loaders import combine_spectra from astropy import units a = specutils.Spectrum1D(spectral_axis=[1., 2., 3.] * units.AA, flux=[1., 2., 3.] * units.Jy) b = specutils.Spectrum1D(spectral_axis=[1e-10, 2e-10, 3e-10] * units.m, flux=[4e-23, 5e-23, 6e-23] * units.Unit('erg s-1 cm-2 Hz-1')) assert np.allclose(a.spectral_axis, b.spectral_axis, atol=0, rtol=1e-10) assert a == combine_spectra(a, None) assert a == combine_spectra(None, a) ab = combine_spectra(a, b) assert isinstance(ab, specutils.Spectrum1D) assert ab.shape == (2, 3) assert ab.flux.unit == units.Jy assert np.allclose([[1, 2, 3], [4, 5, 6]], ab.flux.value) abb = combine_spectra(ab, b) assert isinstance(ab, specutils.Spectrum1D) assert abb.shape == (3, 3) assert abb.flux.unit == units.Jy assert np.allclose([[1, 2, 3], [4, 5, 6], [4, 5, 6]], abb.flux.value) c = specutils.Spectrum1D(spectral_axis=[1., 2., 3., 4.] * units.AA, flux=[1., 2., 3., 4.] * units.Jy) ac = combine_spectra(a, c) assert isinstance(ac, specutils.SpectrumList) aca = combine_spectra(ac, a) assert isinstance(aca, specutils.SpectrumList)
def test_stellar_mass_from_reference_band(): from astropy import units from skypy.galaxy.spectrum import mag_ab, stellar_mass_from_reference_band # Gaussian bandpass bp_lam = np.logspace(0, 4, 1000) * units.AA bp_mean = 1000 * units.AA bp_width = 100 * units.AA bp_tx = np.exp(-( (bp_lam - bp_mean) / bp_width)**2) * units.dimensionless_unscaled band = specutils.Spectrum1D(spectral_axis=bp_lam, flux=bp_tx) # 3 Flat template spectra lam = np.logspace(0, 4, 1000) * units.AA A = np.array([[2], [3], [4]]) flam = A * 0.10884806248538730623 * units.Unit('erg s-1 cm-2 AA') / lam**2 templates = specutils.Spectrum1D(spectral_axis=lam, flux=flam) # Absolute magnitudes for each template Mt = mag_ab(templates, band) # Using the identity matrix for the coefficients yields trivial test cases coeff = np.diag(np.ones(3)) # Using the absolute magnitudes of the templates as reference magnitudes # should return one solar mass for each template. stellar_mass = stellar_mass_from_reference_band(coeff, templates, Mt, band) truth = 1 np.testing.assert_allclose(stellar_mass, truth) # Solution for given magnitudes without template mixing Mb = np.array([10, 20, 30]) stellar_mass = stellar_mass_from_reference_band(coeff, templates, Mb, band) truth = np.power(10, -0.4 * (Mb - Mt)) np.testing.assert_allclose(stellar_mass, truth)
def combine_spectra(a, b): '''combine two spectra''' if a is None or b is None: return a or b if isinstance(a, specutils.SpectrumList) or isinstance(b, specutils.SpectrumList): a = a if isinstance(a, specutils.SpectrumList) else specutils.SpectrumList([a]) b = b if isinstance(b, specutils.SpectrumList) else specutils.SpectrumList([b]) return specutils.SpectrumList(a + b) if (len(a.spectral_axis) == len(b.spectral_axis) and np.allclose(a.spectral_axis, b.spectral_axis, atol=0, rtol=1e-10) and a.flux.unit.is_equivalent(b.flux.unit)): flux_a = np.atleast_2d(a.flux.value) flux_b = np.atleast_2d(b.flux.to_value(a.flux.unit)) if flux_a.shape[1:] == flux_b.shape[1:]: return specutils.Spectrum1D(spectral_axis=a.spectral_axis, flux=np.concatenate([flux_a, flux_b])*a.flux.unit) return specutils.SpectrumList([a, b])
def skypy_data_loader(module, name, *tags): '''load data from the skypy data package''' # result is spectrum or list of spectra spectra = None # load each tag separately for tag in tags: # get resource filename from module, name, and tag try: filename = resource_filename(f'skypy-data.{module}', f'{name}_{tag}.ecsv') except ModuleNotFoundError as exc: message = str("No module named 'skypy-data'. To install:\n" "pip install skypy-data@https://github.com/" "skypyproject/skypy-data/archive/master.tar.gz") raise ModuleNotFoundError(message) from exc # load the data file data = astropy.table.Table.read(filename, format='ascii.ecsv') # get the spectral axis spectral_axis = data['spectral_axis'].quantity # load all templates flux_unit = data['flux_0'].unit fluxes = [] while 'flux_%d' % len(fluxes) in data.colnames: fluxes.append(data['flux_%d' % len(fluxes)].quantity.to_value(flux_unit)) fluxes = np.squeeze(fluxes) * flux_unit # construct the Spectrum1D spectrum = specutils.Spectrum1D(spectral_axis=spectral_axis, flux=fluxes) # combine with existing spectra = combine_spectra(spectra, spectrum) return spectra
def test_from_spectrum1d_Empirical1D_source(self): import specutils lamb = [1000, 5000, 10000] * u.AA flux = [0, -0.5e-17, 5.6e-17] * units.FLAM spec = specutils.Spectrum1D(spectral_axis=lamb, flux=flux) spec.meta['source'] = [1, 2, 3] with pytest.warns(AstropyUserWarning, match=r'contained negative flux or throughput'): sp = SourceSpectrum.from_spectrum1d(spec, keep_neg=False) w = sp.waveset y = sp(w, flux_unit=units.FLAM) assert isinstance(sp.model, Empirical1D) assert sp.meta['header']['source'] == spec.meta['source'] assert_quantity_allclose(w, lamb) assert_quantity_allclose(y, [0, 0, 5.6e-17] * units.FLAM) # Ensure metadata is copied, not referenced spec.meta['source'][1] = 99 assert sp.meta['header']['source'] == [1, 2, 3] sp.meta['header']['source'][0] = 100 assert spec.meta['source'] == [1, 99, 3]
def measure_EW_simple(waves, fluxes, unc, line, wave_range, feat_type, line_width=[0., 0.], plot=False, file=None, verbose=False, name='Unknown Line', diag_path=None): feat_type = feat_type.lower() #check to see if there's flux if np.all(np.abs(fluxes) <= 2 * unc): print('Warning! No flux in region for {}!'.format(name)) res = 'No Flux' return res, res feature_waves = waves * u.AA feature_flux = fluxes * u.erg / (u.AA * u.s * (u.cm)**2) uncertainties = unc feature_unc = astropy.nddata.StdDevUncertainty(unc * u.erg / (u.AA * u.s * (u.cm)**2)) feature_spec = specutils.Spectrum1D(spectral_axis=feature_waves, flux=feature_flux, uncertainty=feature_unc) continuum_fit = specutils.fitting.fit_generic_continuum(feature_spec)( feature_spec.spectral_axis) #looping to refine the continuum fit #initial mask around the feature mask = np.ones(len(feature_flux), dtype='bool') mask[(feature_waves.value > line_width[0]) & (feature_waves.value < line_width[1])] = False subtr_spec = feature_spec - continuum_fit for i in range(5): masked_spec = specutils.Spectrum1D(spectral_axis=feature_waves[mask], flux=feature_flux[mask], uncertainty=feature_unc[mask]) continuum_model = specutils.fitting.fit_generic_continuum(masked_spec) continuum_fit = continuum_model(feature_spec.spectral_axis) subtr_spec = feature_spec - continuum_fit mask = np.abs(subtr_spec.flux.value) < 3 * uncertainties if verbose: plt.plot(feature_waves, continuum_fit) plt.plot(feature_waves, feature_spec.flux) plt.title('refine {}'.format(i)) plt.show() continuum_fit = continuum_model(feature_spec.spectral_axis) subtr_spec = feature_spec - continuum_fit if plot or (diag_path is not None): plt.figure(figsize=[12, 7]) plt.plot(feature_spec.wavelength, feature_spec.flux) plt.plot(feature_spec.wavelength, continuum_fit) #plt.xlabel('Wavelength') #plt.ylabel('Flux Density') plt.title('Continuum fit of {}'.format(name)) if isinstance(file, str): plt.savefig('kastclassify_EW_continuum_' + file) if diag_path is not None: plt.savefig(diag_path + '{}_EW_continuum_fit.png'.format(name)) if plot: plt.show() plt.close() #finding line center near_line_waves = feature_waves[(feature_waves.value >= (line - 10)) & (feature_waves.value <= (line + 10))] near_line_fluxes = subtr_spec.flux.value[ (feature_waves.value >= (line - 10)) & (feature_waves.value <= (line + 10))] if feat_type == 'absorption': line = near_line_waves.value[near_line_fluxes == np.amin( near_line_fluxes)] elif feat_type == 'emission': line == near_line_waves.value[near_line_fluxes == np.amax( near_line_fluxes)] else: print('WARNING! Feature type {} is unknown'.format(feat_type)) res = 'Unknown Feature Type' return res, res #create continuum normalized spectrum and continuum subtracted spectrum norm_spec = feature_spec / continuum_fit #re-centering using subtracted spectrum #find all features in this spectrum line_info = specutils.fitting.find_lines_threshold(subtr_spec) if len(line_info) == 0: print('Warning! No features detected for {}!'.format(name)) res = 'No Feature Detected' return res, res try: #the actual line center is the closest feature to our expected center nearest = np.argmin( np.abs(line_info[line_info['line_type'] == feat_type] ['line_center'].value - line)) except: print('Warning! No features detected for {}!'.format(name)) res = 'No Feature Detected' return res, res line_center = line_info[line_info['line_type'] == feat_type][nearest]['line_center'].value #if the line width is not defined, calculate it #calculated as 1.5 * FWHM if line_width[0] == 0.: near_line_waves = feature_waves[(feature_waves.value >= (line - 15)) & (feature_waves.value <= (line + 15))].value near_line_fluxes = subtr_spec.flux.value[ (feature_waves.value >= (line - 15)) & (feature_waves.value <= (line + 15))] if feat_type == 'absorption': near_line_fluxes = np.abs(near_line_fluxes) elif feat_type == 'emission': pass else: print('WARNING! Feature type {} is unknown'.format(feat_type)) res = 'Unknown Feature Type' return res, res max_flx = np.amax(near_line_fluxes) half_max = max_flx / 2 outside = near_line_fluxes < half_max try: lower_bound = max(near_line_waves[(near_line_waves < line_center) & outside]) upper_bound = min(near_line_waves[(near_line_waves > line_center) & outside]) except: print('Warning! No features detected for {}!'.format(name)) res = 'No Feature Detected' return res, res FWHM = upper_bound - lower_bound line_width[0] = 1.5 * FWHM line_width[1] = 1.5 * FWHM #pick out the region containing the feature region = specutils.SpectralRegion((line_center - line_width[0]) * u.AA, (line_center + line_width[1]) * u.AA) #calculate the width EW = specutils.analysis.equivalent_width(norm_spec, regions=region) if plot or (diag_path is not None): fig, ax = plt.subplots(1, figsize=[12, 7]) ax.plot(norm_spec.wavelength, norm_spec.flux, label=EW) ax.axvline(line_center - line_width[0]) ax.axvline(line_center + line_width[1]) rect = matplotlib.patches.Rectangle((line_center - EW.value / 2, 0), EW.value, 1, hatch='x', fill=False) ax.add_patch(rect) ax.set_xlabel('Wavelength ({})'.format(u.AA)) ax.set_ylabel('Normalized Flux Density') ax.set_title('Equivalent Width of {}'.format(name)) fig.legend() if isinstance(file, str): fig.savefig('kastclassify_EW_width_' + file) if diag_path is not None: fig.savefig(diag_path + '{}_EW_measurement.png'.format(name)) if plot: fig.show() plt.close(fig) return EW, line_center
def sp_correction(nuclear_spectrum, external_spectrum): """Stellar Population substraction. The spectral continuum of Type 2 Seyfert galaxies in the K band (19.-2.5 $mu$m) is composed by the stellar population component and the hot dust component. The first one is the sum of the Planck functions of all the stars in the host galaxy and can be represented by a spectrum extracted at a prudential distance from the nucleus, where the emission is expected to be dominated by the stellar population. In sp_correction this is introduced in the parameter "external spectrum". The stellar population dominated spectrum must be substracted from the nuclear spectrum in order to obtain the hot dust component in the nuclear spectrum. The excess obtained from the substraction is expected to have blackbody-shape. The operations applied to prepare the nuclear spectrum for fitting are: 1) normalization using the mean value of the flux for both spectra 2) substraction of the external spectrum flux from the nuclear spectrum flux. Parameters ---------- nuclear_spectrum: NirdustSpectrum object Instance of NirdusSpectrum containing the nuclear spectrum. external_spectrum: NirdustSpectrum object Instance of NirdusSpectrum containing the external spectrum. Returns ------- out: NirsdustSpectrum object Returns a new instance of the class NirdustSpectrum containing the nuclear spectrum ready for blackbody fitting. """ normalized_nuc = nuclear_spectrum.normalize() normalized_ext = external_spectrum.normalize() dif = len(normalized_nuc.spec1d.spectral_axis) - len( normalized_ext.spec1d.spectral_axis) if dif == 0: flux_resta = (normalized_nuc.spec1d.flux - normalized_ext.spec1d.flux) + 1 new_spectral_axis = nuclear_spectrum.spectral_axis elif dif < 0: new_ext = normalized_ext[-dif:] flux_resta = (normalized_nuc.spec1d.flux - new_ext.spec1d.flux) + 1 new_spectral_axis = external_spectrum.spectral_axis[-dif:] elif dif > 0: new_nuc = normalized_nuc[dif:] flux_resta = (new_nuc.spec1d.flux - normalized_ext.spec1d.flux) + 1 new_spectral_axis = nuclear_spectrum.spectral_axis[dif:] substracted_1d_spectrum = su.Spectrum1D(flux_resta, new_spectral_axis) new_freq_axis = substracted_1d_spectrum.spectral_axis.to(u.Hz) kwargs = attr.asdict(normalized_nuc) kwargs.update(spec1d=substracted_1d_spectrum, frequency_axis=new_freq_axis) return NirdustSpectrum(**kwargs)
def spectrum( flux, header, z=0, dispersion_key="CD1_1", first_wavelength="CRVAL1", dispersion_type="CTYPE1", **kwargs, ): """Instantiate a NirdustSpectrum object from FITS parameters. Parameters ---------- flux: Quantity Intensity for each pixel in arbitrary units. header: FITS header Header of the spectrum. z: float Redshif of the galaxy. dispersion_key: str Header keyword that gives dispersion in Å/pix. Default is 'CD1_1' first_wavelength: str Header keyword that contains the wavelength of the first pixel. Default is ``CRVAL1``. dispersion_type: str Header keyword that contains the dispersion function type. Default is ``CTYPE1``. Return ------ spectrum: ``NirsdustSpectrum`` Return a instance of the class NirdustSpectrum with the entered parameters. """ if header[dispersion_key] <= 0: raise ValueError("dispersion must be positive") spectrum_length = len(flux) spectral_axis = ((header[first_wavelength] + header[dispersion_key] * np.arange(0, spectrum_length)) / (1 + z) * u.AA) spec1d = su.Spectrum1D(flux=flux * u.adu, spectral_axis=spectral_axis, **kwargs) frequency_axis = spec1d.spectral_axis.to(u.Hz) return NirdustSpectrum( header=header, z=z, spectrum_length=spectrum_length, dispersion_key=dispersion_key, first_wavelength=first_wavelength, dispersion_type=dispersion_type, spec1d=spec1d, frequency_axis=frequency_axis, )