def get_dv_closest_z(z1, z2, give_ids=False): """Returns an array of same lenght as z1, with the velocity difference (in km/s) associates to the closest redshift in z2, at restframe given by z2. (Using relativistic approximation for flat-space time; see ltu.dv_from_z() function) If give_ids is True , it also returns the indices of z2 where the difference is minimum. """ z1 = np.array(z1) z2 = np.array(z2) dv = [] inds = [] for z in z1: dv_aux = ltu.dv_from_z(z, z2) # find minimum difference cond = np.fabs(dv_aux) == np.min(np.fabs(dv_aux)) ind = np.where(cond)[0][0] # append dv dv += [dv_aux[ind]] inds += [ind] dv = np.array(dv) if give_ids: return dv, inds else: return dv
def pairs(self, sep, dv): """ Generate a pair catalog Parameters ---------- sep : Angle or Quantity dv : Quantity Offset in velocity. Positive for projected pairs (i.e. dz > input value) Returns ------- """ # Checks if not isinstance(sep, (Angle, Quantity)): raise IOError("Input radius must be an Angle type, e.g. 10.*u.arcsec") if not isinstance(dv, (Quantity)): raise IOError("Input velocity must be a quantity, e.g. u.km/u.s") # Match idx, d2d, d3d = match_coordinates_sky(self.coords, self.coords, nthneighbor=2) close = d2d < sep # Cut on redshift if dv > 0.: # Desire projected pairs zem1 = self.cat['zem'][close] zem2 = self.cat['zem'][idx[close]] dv12 = ltu.dv_from_z(zem1,zem2) gdz = np.abs(dv12) > dv # f/g and b/g izfg = dv12[gdz] < 0*u.km/u.s ID_fg = self.cat[self.idkey][close][gdz][izfg] ID_bg = self.cat[self.idkey][idx[close]][gdz][izfg] else: pdb.set_trace() # Reload return ID_fg, ID_bg
def reset(self): """ Update all the values """ #self._data['zlim'] = self._zlim if self._wrest is not None: self._wvlim = self._wrest*(1+np.array(self._zlim)) self._vlim = ltu.dv_from_z(self._zlim, self._z)
def get_vmnx(components): zlim_sys = ltu.z_from_dv(self.vlim, self.zabs, rel=False) zmin, zmax = zlim_sys for component in components: zlim_comp = ltu.z_from_dv(component.vlim, component.zcomp, rel=False) zmin = min(zmin, zlim_comp[0]) zmax = max(zmax, zlim_comp[1]) # Convert back to velocities return ltu.dv_from_z([zmin,zmax], self.zabs, rel=False)
def repr_joebvp(self, specfile, flags=(2,2,2), b_default=10*u.km/u.s, z_sys = None): """ String representation for JOEBVP (line fitting software). Parameters ---------- specfile : str Name of the spectrum file flags : tuple of ints, optional Flags (nflag, bflag, vflag). See JOEBVP input for details about these flags. b_default : Quantity, optional Doppler parameter value adopted in case an absorption line within the component has not set this attribute Default is 10 km/s. z_sys : float, optional Systemic redshift if different from zcomp; if provided, velocities will be transformed to this frame Returns ------- repr_joebvp : str May contain multiple "\n" (1 per absline within component) """ # Reference: (note that comment column must be the last one) # specfile|restwave|zsys|col|bval|vel|nflag|bflag|vflag|vlim1|vlim2|wobs1|wobs2|z_comp|trans|rely|comment s = '' for aline in self._abslines: s += '{:s}|{:.5f}|'.format(specfile, aline.wrest.to('AA').value) logN = aline.attrib['logN'] b_val = aline.attrib['b'].to('km/s').value if b_val == 0: # set the default b_val = b_default.to('km/s').value if z_sys is None: z_sys = self.zcomp vel = 0. else: vel = ltu.dv_from_z(self.zcomp,z_sys).value # write string s += '{:.8f}|{:.4f}|{:.4f}|{:.4f}|'.format(z_sys, logN, b_val,vel) # `vel` is set to 0. because zsys is zcomp s += '{}|{}|{}|'.format(int(flags[0]), int(flags[1]), int(flags[2])) vlim = aline.limits.vlim.to('km/s').value wvlim = aline.limits.wvlim.to('AA').value s += '{:.4f}|{:.4f}|{:.5f}|{:.5f}|'.format(vlim[0], vlim[1], wvlim[0], wvlim[1]) s += '{:.8f}|{:s}|{:s}|{:s}'.format(self.zcomp, aline.ion_name, self.reliability, self.comment) # zcomp again here # if len(self.comment) > 0: # s += '# {:s}'.format(self.comment) s += '\n' return s
def test_dv_from_z(): dv = ltu.dv_from_z(2.1, 2.) assert dv.unit == u.km / u.s np.testing.assert_allclose(dv.value, 9826.620063406788, rtol=1e-6) dv = ltu.dv_from_z([2.1, 2.1, 2.1], 2.) np.testing.assert_allclose(dv.value, [9826.620063406788] * 3, rtol=1e-6) # non-relativistic dv = ltu.dv_from_z(2.1, 2., rel=False) np.testing.assert_allclose(dv.value, 9993.08193333334, rtol=1e-6) # test expected errors with pytest.raises(IOError): ltu.dv_from_z('z_not_a_float_or_array', 1.) with pytest.raises(IOError): ltu.dv_from_z(2.1, 'zref_not_a_float_nor_array') with pytest.raises(IOError): ltu.dv_from_z(np.array([2.1, 2.1, 2.1]), np.array([2., 2.])) # wrong shape for zref
def update_component_vel(self): """Change the velocities of each component to rest frame of zsys """ from linetools.analysis.zlimits import zLimits for i, comp in enumerate(self._components): dv = ltu.dv_from_z(comp.zcomp, self.zabs) zmin = ltu.z_from_dv(comp.limits.vlim[0] + dv, self.zabs) zmax = ltu.z_from_dv(comp.limits.vlim[1] + dv, self.zabs) newzlim = zLimits(self.zabs, (zmin, zmax)) comp.limits = newzlim comp.attrib['vel'] = comp.attrib['vel'] + dv
def associate_redshifts(z1, z2, dv): """Returns an array of same lenght as z1, with a 1 if z1 redshift lie within dv from any of the redshifts given by the array z2; otherwise it has a value of 0""" z1 = np.array(z1) z2 = np.array(z2) association = np.zeros(len(z1)) for z in z2: dv_aux = np.fabs(ltu.dv_from_z(z1, z)) association = np.where(dv_aux < dv, 1, association) return association
def cgm_from_galaxy_igmsystems(galaxy, igmsystems, R_max=300 * u.kpc, dv_max=400 * u.km / u.s, cosmo=None, **kwargs): """ Generate a list of CGMAbsSys objects given an input galaxy and a list of IGMSystems Parameters ---------- galaxy : Galaxy igmsystems : list list of IGMSystems R_max : Quantity Maximum projected separation from sightline to galaxy dv_max Maximum velocity offset between system and galaxy Returns ------- cgm_list : list list of CGM objects generated """ from pyigm.cgm.cgm import CGMAbsSys # Cosmology if cosmo is None: cosmo = cosmology.Planck15 # R rho, angles = calc_rho(galaxy, igmsystems, cosmo) # dv igm_z = np.array([igmsystem.zabs for igmsystem in igmsystems]) dv = ltu.dv_from_z(igm_z, galaxy.z) # Rules match = np.where((rho < R_max) & (np.abs(dv) < dv_max))[0] if len(match) == 0: print("No CGM objects match your rules") return [] else: # Loop to generate cgm_list = [] for imatch in match: cgm = CGMAbsSys(galaxy, igmsystems[imatch], cosmo=cosmo, **kwargs) cgm_list.append(cgm) # Return return cgm_list
def plot_vel(ax, spec, iline, z, dvlims, complist=None, fwhm=3, min_ew_blends=0.01*u.AA): # first normalize if not spec.normed: spec.normalize(co=spec.co) # first identify good components good_comps = [] good_comps_aux = ltiu.get_components_at_z(complist, z, dvlims) for comp in good_comps_aux: for aline in comp._abslines: if aline.name == iline['name']: good_comps += [comp] break # bad comps will be those unrelated to the given redshift/transition bad_comps = [] for comp in complist: if comp not in good_comps: bad_comps += [comp] # only good comps will have a model if len(good_comps) > 0: # import pdb; pdb.set_trace() model_spec = lav.voigt_from_components(spec.wavelength, good_comps, fwhm=fwhm) else: model_spec = XSpectrum1D.from_tuple((spec.wavelength, np.ones(spec.npix))) # main plot velo = ltu.dv_from_z(spec.wavelength/iline['wrest'] - 1. , z) ax.plot(velo, spec.flux, drawstyle='steps-mid', color=COLOR_FLUX) plot_spec_complist(ax, velo, spec, bad_comps, min_ew = min_ew_blends, color=COLOR_BLEND, lw=2, drawstyle='steps-mid') plot_spec_complist(ax, velo, model_spec, good_comps, min_ew = None, color=COLOR_MODEL, lw=1, ls='-') if spec.sig_is_set: ax.plot(velo, spec.sig, drawstyle='steps-mid', color=COLOR_SIG, lw=0.5) # Zero velocity line ax.plot([0., 0.], [-1e9, 1e9], ':', color='gray') # Unity flux level line ax.plot([-1e9, 1e9], [1, 1], ':', color='b', lw=0.5) # Zero flux level line ax.plot([-1e9, 1e9], [0, 0], '--', color='k', lw=1)
def get_components_at_z(complist, z, dvlims): """In a given list of AbsComponents, it finds the ones that are within dvlims from a given redshift and returns a list of those. Parameters ---------- complist : list List of AbsComponents z : float Redshift to search for components dvlims : Quantity array Rest-frame velocity limits around z to look for components Returns ------- components_at_z : list List of AbsComponents in complist within dvlims from z """ # check input if not isinstance(complist[0], AbsComponent): raise IOError('complist must be a list of AbsComponents.') if len(dvlims) != 2: raise IOError( 'dvlims must be a Quantity array of velocity limits (vmin, vmax).') else: try: dvlims_kms = dvlims.to('km/s') except u.UnitConversionError: raise IOError('dvlims must have velocity units.') good_complist = [] for comp in complist: dv_comp = ltu.dv_from_z(comp.zcomp, z) if (dv_comp >= dvlims[0]) and (dv_comp <= dvlims[1]): good_complist += [comp] return good_complist
def get_components_at_z(complist, z, dvlims): """In a given list of AbsComponents, it finds the ones that are within dvlims from a given redshift and returns a list of those. Parameters ---------- complist : list List of AbsComponents z : float Redshift to search for components dvlims : Quantity array Rest-frame velocity limits around z to look for components Returns ------- components_at_z : list List of AbsComponents in complist within dvlims from z """ # check input if not isinstance(complist[0], AbsComponent): raise IOError('complist must be a list of AbsComponents.') if len(dvlims) != 2: raise IOError('dvlims must be a Quantity array of velocity limits (vmin, vmax).') else: try: dvlims_kms = dvlims.to('km/s') except u.UnitConversionError: raise IOError('dvlims must have velocity units.') good_complist = [] for comp in complist: dv_comp = ltu.dv_from_z(comp.zcomp, z) if (dv_comp >= dvlims[0]) and (dv_comp <= dvlims[1]): good_complist += [comp] return good_complist
def cgm_from_galaxy_igmsystems(galaxy, igmsystems, rho_max=300 * u.kpc, dv_max=400 * u.km / u.s, cosmo=None, dummysys=False, dummyspec=None, verbose=True, **kwargs): """ Generate a list of CGMAbsSys objects given an input galaxy and a list of IGMSystems Parameters ---------- galaxy : Galaxy igmsystems : list list of IGMSystems rho_max : Quantity Maximum projected separation from sightline to galaxy dv_max Maximum velocity offset between system and galaxy dummysys: bool, optional If True, instantiate CGMAbsSys even if no match is found in igmsystems dummyspec : XSpectrum1D, optional Spectrum object to attach to dummy AbsLine/AbsComponent objects when adding IGMSystems if dummysys is True. Returns ------- cgm_list : list list of CGM objects generated """ from pyigm.cgm.cgm import CGMAbsSys # Cosmology if cosmo is None: cosmo = cosmology.Planck15 if dummysys is True: if dummyspec is None: dummyspec = igmsystems[0]._components[0]._abslines[0].analy['spec'] dummycoords = igmsystems[0].coord # R -- speed things up rho, angles = calc_cgm_rho(galaxy, igmsystems, cosmo, **kwargs) if len(igmsystems) == 1: # Kludge rho = u.Quantity([rho]) angles = u.Quantity([angles]) # dv igm_z = np.array([igmsystem.zabs for igmsystem in igmsystems]) dv = ltu.dv_from_z(igm_z, galaxy.z) # Rules match = np.where((rho < rho_max) & (np.abs(dv) < dv_max))[0] ### If none, see if some system has a component that's actually within dv_max if (len(match) == 0) & (rho[0] < rho_max): zcomps = [] sysidxs = [] for i, csys in enumerate(igmsystems): thesezs = [comp.zcomp for comp in csys._components] sysidxs.extend([i] * len(thesezs)) zcomps.extend(thesezs) zcomps = np.array(zcomps) sysidxs = np.array(sysidxs) dv_comps = ltu.dv_from_z(zcomps, galaxy.z) match = np.unique(sysidxs[np.where(np.abs(dv_comps) < dv_max)[0]]) if len(match) == 0: if dummysys is False: print( "No IGMSystem paired to this galaxy. CGM object not created.") return [] else: if verbose: print("No IGMSystem match found. Attaching dummy IGMSystem.") dummysystem = IGMSystem(dummycoords, galaxy.z, vlim=None) dummycomp = AbsComponent(dummycoords, (1, 1), galaxy.z, [-100., 100.] * u.km / u.s) dummycomp.flag_N = 3 dummyline = AbsLine( 'HI 1215', **kwargs) # Need an actual transition for comp check dummyline.analy['spec'] = dummyspec dummyline.attrib['coord'] = dummycoords dummycomp.add_absline(dummyline, chk_vel=False, chk_sep=False) dummysystem.add_component(dummycomp, chk_vel=False, chk_sep=False) cgm = CGMAbsSys(galaxy, dummysystem, cosmo=cosmo, **kwargs) cgm_list = [cgm] else: # Loop to generate cgm_list = [] for imatch in match: # Instantiate new IGMSystem # Otherwise, updates to the IGMSystem cross-pollinate other CGMs sysmatch = igmsystems[imatch] newisys = sysmatch.copy() # Handle z limits zlim = ltu.z_from_dv((-dv_max.value, dv_max.value) * u.km / u.s, galaxy.z) newlims = zLimits(galaxy.z, zlim.tolist()) newisys.limits = newlims # Allow for components extending beyond dv_max newisys.update_vlim() newisys.update_component_vel() # Finish cgm = CGMAbsSys(galaxy, newisys, cosmo=cosmo, rho=rho[imatch], ang_sep=angles[imatch], **kwargs) cgm_list.append(cgm) # Return return cgm_list
def give_dv(z, zref, **kwargs): """Wrapper for convinience""" print( "This function is now in linetools.utils.dv_from_z(), please avoid using this one." ) return ltu.dv_from_z(z, zref, **kwargs)
def give_dv(z, zref, **kwargs): """Wrapper for convinience""" print("This function is now in linetools.utils.dv_from_z(), please avoid using this one.") return ltu.dv_from_z(z, zref, **kwargs)
def plot_vel(ax, spec, iline, z, dvlims, complist=None, fwhm=3, min_ew_blends=0.01 * u.AA): # first normalize if not spec.normed: spec.normalize(co=spec.co) # first identify good components good_comps = [] good_comps_aux = ltiu.get_components_at_z(complist, z, dvlims) for comp in good_comps_aux: for aline in comp._abslines: if aline.name == iline['name']: good_comps += [comp] break # bad comps will be those unrelated to the given redshift/transition bad_comps = [] for comp in complist: if comp not in good_comps: bad_comps += [comp] # only good comps will have a model if len(good_comps) > 0: # import pdb; pdb.set_trace() model_spec = lav.voigt_from_components(spec.wavelength, good_comps, fwhm=fwhm) else: model_spec = XSpectrum1D.from_tuple( (spec.wavelength, np.ones(spec.npix))) # main plot velo = ltu.dv_from_z(spec.wavelength / iline['wrest'] - 1., z) ax.plot(velo, spec.flux, drawstyle='steps-mid', color=COLOR_FLUX) plot_spec_complist(ax, velo, spec, bad_comps, min_ew=min_ew_blends, color=COLOR_BLEND, lw=2, drawstyle='steps-mid') plot_spec_complist(ax, velo, model_spec, good_comps, min_ew=None, color=COLOR_MODEL, lw=1, ls='-') if spec.sig_is_set: ax.plot(velo, spec.sig, drawstyle='steps-mid', color=COLOR_SIG, lw=0.5) # Zero velocity line ax.plot([0., 0.], [-1e9, 1e9], ':', color='gray') # Unity flux level line ax.plot([-1e9, 1e9], [1, 1], ':', color='b', lw=0.5) # Zero flux level line ax.plot([-1e9, 1e9], [0, 0], '--', color='k', lw=1)
def repr_joebvp(self, specfile, flags=(2, 2, 2), b_default=10 * u.km / u.s, z_sys=None): """ String representation for JOEBVP (line fitting software). Parameters ---------- specfile : str Name of the spectrum file flags : tuple of ints, optional Flags (nflag, bflag, vflag). See JOEBVP input for details about these flags. b_default : Quantity, optional Doppler parameter value adopted in case an absorption line within the component has not set this attribute Default is 10 km/s. z_sys : float, optional Systemic redshift if different from zcomp; if provided, velocities will be transformed to this frame Returns ------- repr_joebvp : str May contain multiple "\n" (1 per absline within component) """ # Reference: (note that comment column must be the last one) # specfile|restwave|zsys|col|bval|vel|nflag|bflag|vflag|vlim1|vlim2|wobs1|wobs2|z_comp|trans|rely|comment s = '' for aline in self._abslines: s += '{:s}|{:.5f}|'.format(specfile, aline.wrest.to('AA').value) logN = aline.attrib['logN'] b_val = aline.attrib['b'].to('km/s').value if b_val == 0: # set the default b_val = b_default.to('km/s').value if z_sys is None: z_sys = self.zcomp vel = 0. else: vel = ltu.dv_from_z(self.zcomp, z_sys).value # write string s += '{:.8f}|{:.4f}|{:.4f}|{:.4f}|'.format( z_sys, logN, b_val, vel) # `vel` is set to 0. because zsys is zcomp s += '{}|{}|{}|'.format(int(flags[0]), int(flags[1]), int(flags[2])) vlim = aline.limits.vlim.to('km/s').value wvlim = aline.limits.wvlim.to('AA').value s += '{:.4f}|{:.4f}|{:.5f}|{:.5f}|'.format(vlim[0], vlim[1], wvlim[0], wvlim[1]) s += '{:.8f}|{:s}|{:s}|{:s}'.format( self.zcomp, aline.ion_name, self.reliability, self.comment) # zcomp again here # if len(self.comment) > 0: # s += '# {:s}'.format(self.comment) s += '\n' return s
def load_data(self, **kwargs): # #q8file = resource_filename('pyigm', 'data/CGM/QPQ/qpq8_all_measured.dat') q8file = self.data_file if self.from_dict: q8file = self.data_file qpq8dict = CGMAbsSurvey.from_json(q8file) ism = LineList('ISM') qpq8dict.build_systems_from_dict(llist=ism) self.survey_data = qpq8dict #self.cgm_abs = qpq8dict.cgm_abs else: qpqdata = Table.read(q8file, format='ascii') #nmax = len(qpqdata) # max number of QSOs q8filecoord = resource_filename('pyigm', 'data/CGM/QPQ/qpq8_pairs.fits') qpqdatacoord = Table.read(q8filecoord) if self.nmax is not None: nmax = self.nmax else: nmax = len(qpqdatacoord) #(qpqdata) # match names with qpqdatacoord qnames = [] for i in range(len(qpqdatacoord)): qname = qpqdatacoord['QSO'][i].strip() qnames.append(qname[-10:]) qnames2 = [] for i in range(len(qpqdata)): qname = qpqdata['Pair'][i] qnames2.append(qname) for j in range(nmax): # i,j # match names with qpqdatacoord i = np.where(np.asarray(qnames2) == qnames[j])[0] # Instantiate the galaxy gal = Galaxy((qpqdatacoord['RAD'][j], qpqdatacoord['DECD'][j]), z=qpqdatacoord['Z_FG'][j]) gal.L_BOL = qpqdatacoord['L_BOL'][j] gal.L_912 = qpqdatacoord['L_912'][j] gal.G_UV = qpqdatacoord['G_UV'][j] gal.zsig = qpqdatacoord['Z_FSIG'][j] * u.km / u.s # Instantiate the IGM System igm_sys = IGMSystem( (qpqdatacoord['RAD_BG'][j], qpqdatacoord['DECD_BG'][j]), qpqdatacoord['Z_FG'][j], [-5500, 5500.] * u.km / u.s, abs_type='CGM') # Redshifts: QSO emission redshifts igm_sys.zem = qpqdatacoord['Z_BG'][j] igm_sys.NHI = qpqdata['HIcol'][i] igm_sys.sig_NHI = [ qpqdata['HIcolhierr'][i], qpqdata['HIcolloerr'][i] ] igm_sys.s2n_lya = qpqdatacoord['S2N_LYA'][j] iname = qpqdata['Pair'][i][0] #+'_'+qpqdata['subsys'][i] # Instantiate rho = qpqdatacoord['R_PHYS'][j] * u.kpc cgabs = CGMAbsSys(gal, igm_sys, name=iname, rho=rho, **kwargs) ### add metal lines ### not included CII*, SiII* lines = [ ['CII 1334'], ## ['CII* 1335'], ['CIV 1548', 'CIV 1550'], ['NI 1134', 'NI 1199'], ['NII 1083'], ['NV 1238', 'NV 1242'], ['OI 1302'], ['OVI 1037'], ['MgI 2852'], ['MgII 2796', 'MgII 2803'], ['AlII 1670'], ['AlIII 1854', 'AlIII 1862'], [ 'SiII 1190', 'SiII 1193', 'SiII 1304', 'SiII 1260', 'SiII 1526', 'SiII 1808' ], ## ['SiII* 1264'], ['SiIII 1206'], ['SiIV 1393', 'SiIV 1402'], [ 'FeII 1608', 'FeII 2344', 'FeII 2374', 'FeII 2382', 'FeII 2586', 'FeII 2600' ], ['FeIII 1122'] ] for kk in i: for icmp in range(len(lines)): abslines = [] for ii in range(len(lines[icmp])): wave0 = float(lines[icmp][ii].split(' ')[1]) ewstr = str(lines[icmp][ii].split(' ')[1]) + 'EW' ewerrstr = str( lines[icmp][ii].split(' ')[1]) + 'EWerr' if ewstr == '1808EW': ewstr = '1808E' if ewerrstr == '1122EWerr': ewerrstr = '122EWerr' if qpqdata[ewstr][kk] != '/': # find z v0 = 0.5 * ( qpqdata['v_lobound'][kk] + qpqdata['v_upbound'][kk]) * u.km / u.s dv = v0 zref = igm_sys.zabs z_cmp = ltu.z_from_dv(dv, zref) ## vlim v1 = qpqdata['v_lobound'][kk] * u.km / u.s z1 = ltu.z_from_dv(v1, zref) v1_cmp = ltu.dv_from_z(z1, z_cmp) v2 = qpqdata['v_upbound'][kk] * u.km / u.s z2 = ltu.z_from_dv(v2, zref) v2_cmp = ltu.dv_from_z(z2, z_cmp) # iline iline = AbsLine(wave0 * u.AA, closest=True, z=z_cmp) iline.attrib['coord'] = igm_sys.coord ## EW iline.attrib['EW'] = float( qpqdata[ewstr][kk]) * u.AA # Rest EW iline.attrib['sig_EW'] = float( qpqdata[ewerrstr][kk]) * u.AA flgew = 1 if iline.attrib[ 'EW'] < 3. * iline.attrib['sig_EW']: flgew = 3 iline.attrib['flag_EW'] = flgew ## column densities colstr = str( lines[icmp][ii].split(' ')[0]) + 'col' colerrstr = str( lines[icmp][ii].split(' ')[0]) + 'colerr' iline.attrib['logN'] = qpqdata[colstr][kk] iline.attrib['sig_logN'] = qpqdata[colerrstr][ kk] abslines.append(iline) if len(abslines) > 0: comp = AbsComponent.from_abslines(abslines, chk_vel=False) comp.limits._vlim = [v1_cmp.value, v2_cmp.value ] * u.km / u.s cgabs.igm_sys.add_component(comp) # add ang_sep qsocoord = SkyCoord(ra=qpqdatacoord['RAD'][j], dec=qpqdatacoord['DECD'][j], unit='deg') bgcoord = SkyCoord(ra=qpqdatacoord['RAD_BG'][j], dec=qpqdatacoord['DECD_BG'][j], unit='deg') cgabs.ang_sep = qsocoord.separation(bgcoord).to('arcsec') self.cgm_abs.append(cgabs)