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 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 dla_stat(DLAs, qsos, vprox=None, buff=3000.*u.km/u.s, zem_min=0., flg_zsrch=0, vmin=0.*u.km/u.s, LLS_CUT=None, partial=False, prox=False, zem_tol=0.03): """ Identify the statistical DLA in a survey Note that this algorithm ignores any existing mask Parameters ---------- DLAs : DLASurvey qsos : Table keys must include RA, DEC, ZEM, Z_START vmin : Quantity vprox maxdz zem_min buff : Quantity Buffer velocity in Proximate analysis [not ready for this] NHI_cut flg_zsrch dz_toler partial : bool, optional Analyze partial LLS? [pLLS] prox : bool, optional Proximate LLS? [PLLS] zem_tol : float, optional Tolerance in zem Returns ------- msk_smpl : bool array True = statistical """ import warnings from astropy.coordinates import SkyCoord, match_coordinates_sky # Check for mask if DLAs.mask is not None: warnings.warn("Resetting mask to None. Be careful here") DLAs.mask = None # DLA msk_smpl = DLAs.zem != DLAs.zem #zmax = ltu.z_from_dv(vprox, qsos['ZEM']) zmin = ltu.z_from_dv(vmin*np.ones(len(qsos)), qsos['Z_START'].data) # vmin must be array-like to be applied to each individual qsos['Z_START'] # Make some lists qsos_coord = SkyCoord(ra=qsos['RA'], dec=qsos['DEC']) dla_coord = DLAs.coord idx, d2d, d3d = match_coordinates_sky(dla_coord, qsos_coord, nthneighbor=1) close = d2d < 1.*u.arcsec for qq, idla in enumerate(DLAs._abs_sys): # In stat? if close[qq]: if np.abs(idla.zem-qsos['ZEM'][idx[qq]]) < zem_tol: if ((idla.zabs >= zmin[idx[qq]]) & (idla.zabs <= qsos['Z_END'][idx[qq]]) & (qsos[idx[qq]]['FLG_BAL'] != 2)): msk_smpl[qq] = True # Return return msk_smpl
def load_HST_ACS(cls, tau_LL=2, sample='stat'): """ Load the LLS survey using HST/ACS by O'Meara et al. 2013, ApJ, 765, 137 Parameters ---------- tau_LL : int, optional Sets sample sample : str, optional Returns ------- lls_survey : IGMSurvey """ if tau_LL == 2: NHI_cut = 17.49 # LLS File lls_fil = pyigm_path + '/data/LLS/HST/lls_acs_stat_LLS.fits.gz' lls = Table.read(lls_fil) # Generate coords scoords = [ lls['QSO_RA'][ii] + ' ' + lls['QSO_DEC'][ii] for ii in range(len(lls)) ] coords = SkyCoord(scoords, unit=(u.hourangle, u.deg)) lls['RA'] = coords.ra.value lls['DEC'] = coords.dec.value # Read lls_survey = cls.from_sfits(lls, coords=coords) lls_survey.ref = 'HST-ACS' # QSOs file qsos_fil = pyigm_path + '/data/LLS/HST/lls_acs_qsos_sn1020.fits.gz' qsos = Table.read(qsos_fil) lls_survey.sightlines = qsos # Z_START, Z_END # Using tau=2 if tau_LL == 2: lls_survey.sightlines['Z_START'] = np.maximum( qsos['ZT2'], qsos['ZLLS'][:, 0]) else: pdb.set_trace() # zend zend = ltu.z_from_dv(-3000 * u.km / u.s * np.ones(len(qsos)), lls_survey.sightlines['ZEM']) lls_survey.sightlines['Z_END'] = zend # Stat me mask = lls_stat(lls_survey, NHI_cut=NHI_cut) if sample == 'stat': lls_survey.mask = mask else: lls_survey.mask = ~mask # Return print('HST-ACS: Loaded') return lls_survey
def set(self, inp, chk_z=False):#, itype='zlim'): """ Set (or reset) limits relative to self._z but does not change self._z Parameters ---------- inp : tuple, list, or Quantity array * If floats -> zlim : Redshift limits * If Quantity array with length units -> wvlim : Wavelength limits * If Quantity array with speed units -> vlim : Velocity limits chk_z : bool, optional Demand that zlim bound z Often not desired as z can be somewhat arbitrary Returns ------- """ # Checks if not isinstance(inp, (tuple, list, Quantity)): raise IOError("Input must be tuple, list or Quantity.") #if np.isclose(self._z, 0.): # warnings.warn("Redshift=0. If this is unexpected, set _z and reset limits") if isinstance(inp[0], float): # assume zlim self._zlim = inp elif isinstance(inp[0], Quantity): # may be wvlim or vlim if inp[0].cgs.unit == u.cm: self._zlim = (inp/self._wrest).decompose().to( u.dimensionless_unscaled).value - 1. elif inp[0].cgs.unit == u.cm/u.s: self._zlim = ltu.z_from_dv(inp, self._z) else: raise IOError("Quantity must be length or speed.") else: raise IOError("Input must be floats or Quantities.") # Check if chk_z: if (self._zlim[0] > self._z) or (self._zlim[1] < self._z): #import pdb; pdb.set_trace() raise IOError("Invalid input. `zlim` does not bound `z`.") # Reset self.reset()
def set(self, inp, chk_z=False):#, itype='zlim'): """ Over-ride = to re-init values Parameters ---------- inp : tuple, list, or Quantity array * If floats -> zlim : Redshift limits * If Quantity array with length units -> wvlim : Wavelength limits * If Quantity array with speed units -> vlim : Velocity limits chk_z : bool, optional Demand that zlim bound z Often not desired as z can be somewhat arbitrary Returns ------- """ # Checks if not isinstance(inp, (tuple, list, Quantity)): raise IOError("Input must be tuple, list or Quantity.") if np.isclose(self._z, 0.): warnings.warn("Redshift=0. If this is unexpected, set _z and reset limits") if isinstance(inp[0], float): # assume zlim self._zlim = inp elif isinstance(inp[0], Quantity): # may be wvlim or vlim try: # assume wvlim self._zlim = (inp/self._wrest).decompose().to( u.dimensionless_unscaled).value - 1. except UnitConversionError: try: # assume vlim self._zlim = ltu.z_from_dv(inp, self._z) except ValueError: raise IOError("Quantity must be length or speed.") else: raise IOError("Input must be floats or Quantities.") # Check if chk_z: if (self._zlim[0] > self._z) or (self._zlim[1] < self._z): #import pdb; pdb.set_trace() raise IOError("Invalid input. `zlim` does not bound `z`.") # Reset self.reset()
def test_z_from_dv(): z = ltu.z_from_dv(1000. * u.km / u.s, 2.) np.testing.assert_allclose(z, 2.0100236684175417)
def __init__(self, radec, Zion, zcomp, vlim, Ej=0. / u.cm, A=None, Ntup=None, comment='', name=None, stars=None, reliability='none'): """ Initiator Parameters ---------- radec : tuple or SkyCoord (RA,DEC) in deg or astropy.coordinate.SkyCoord Zion : tuple Atomic number, ion -- (int,int) e.g. (8,1) for OI Note: (-1, -1) is special and is meant for moleculer (e.g. H2) This notation will most likely change in the future. zcomp : float Absorption component redshift vlim : Quantity array Velocity limits of the component w/r to `z` e.g. [-300,300]*u.km/u.s A : int, optional Atomic mass -- used to distinguish isotopes Ntup : tuple (int,float,two-element list,tuple or array) (flag_N, logN, sig_logN) flag_N : Flag describing N measurement (0: no info; 1: detection; 2: saturated; 3: non-detection) logN : log10 N column density sig_logN : Error in log10 N. Two elements are expected but not required Ej : Quantity, optional Energy of lower level (1/cm) stars : str, optional asterisks to add to name, e.g. '**' for CI** Required if name=None and Ej>0. reliability : str, optional Reliability of AbsComponent 'a' - reliable 'b' - possible 'c' - uncertain 'none' - not defined (default) comment : str, optional A comment, default is `` """ # Required self.coord = ltu.radec_to_coord(radec) self.Zion = Zion # Limits zlim = ltu.z_from_dv(vlim, zcomp) self.limits = zLimits(zcomp, zlim.tolist()) # Attributes self.attrib = init_attrib.copy() # Optional self.A = A self.Ej = Ej self.stars = stars self.comment = comment if Ntup is not None: self.attrib['flag_N'] = Ntup[0] self.attrib['logN'] = Ntup[1] if isiterable(Ntup[2]): self.attrib['sig_logN'] = np.array(Ntup[2]) else: self.attrib['sig_logN'] = np.array([Ntup[2]] * 2) _, _ = ltaa.linear_clm(self.attrib) # Set linear quantities # Name if (name is None) and (self.Zion != (-1, -1)): iname = ions.ion_to_name(self.Zion, nspace=0) if self.Ej.value > 0: # Need to put *'s in name if stars is not None: iname += stars else: warnings.warn( "No stars provided. Adding one because Ej > 0.") iname += '*' self.name = '{:s}_z{:0.5f}'.format(iname, self.zcomp) elif (name is None) and (self.Zion == (-1, -1)): self.name = 'mol_z{:0.5f}'.format(self.zcomp) else: self.name = name # reliability if reliability not in ['a', 'b', 'c', 'none']: raise ValueError( "Input reliability `{}` not valid.".format(reliability)) self.reliability = reliability # AbsLines self._abslines = []
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)
def cgmabssys_from_sightline_field(field, sightline, rho_max=300. * u.kpc, minz=0.001, maxz=None, dv_max=400. * u.km / u.s, embuffer=None, dummysys=True, dummyspec=None, linelist=None, debug=False, **kwargs): """Instantiate list of CgmAbsSys objects from IgmgGalaxyField and IGMSightline. Parameters ---------- field : IgmGalaxyField sightline : IGMSightline rho_max : Quantity, optional Maximum impact parameter for associated galaxies minz : float, optional Minimum redshift for galaxy/absorber search maxz : float, optional Maximum redshift for galaxy/absorber search dv_max : Quantity, optional Maximum galaxy-absorber velocity separation embuffer : Quantity, optional Velocity buffer between background source (e.g., QSO) and CGMAbsSys dummysys : bool, optional Passed on to 'cgm_from_galaxy_igmsystems()'. If True, create CGMAbsSyS even if no matching IGMSystem is found in any sightline for some galaxy. dummyspec : XSpectrum1D, optional Spectrum object to attach to dummy AbsLine/AbsComponent objects when adding IGMSystems if dummysys is True linelist : LineList, optional ListList from which to add dummy line in case of no IGMSystem match Returns ------- cgmabslist : list List of CgmAbsSys objects """ if dummyspec is None: dummyspec = sightline._abssystems[0]._components[0]._abslines[0].analy[ 'spec'] if linelist is None: from linetools.spectralline import AbsLine linelist = LineList('ISM') if embuffer is not None: try: bufmax = ltu.z_from_dv(-embuffer, field.zem) if maxz is not None: zmax = np.max(bufmax, maxz) else: zmax = bufmax except: zmax = maxz else: zmax = maxz closegals = get_close_galaxies(field, rho_max, minz, zmax) if debug: closegals = closegals[0:10] cgmabslist = [] for i, gal in enumerate(closegals): #print('i={:d}'.format(i)) galobj = Galaxy((gal['RA'], gal['DEC']), z=gal['Z']) cgmobj = cgm_from_galaxy_igmsystems(galobj, sightline._abssystems, dv_max=dv_max, dummysys=dummysys, dummyspec=dummyspec, rho_max=rho_max, linelist=linelist, **kwargs) cgmabslist.extend(cgmobj) return cgmabslist
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 writelinepars(fitpars, fiterrors, parinfo, specfile, outfilename, linecmts=None): ''' Write fit parameters out to file. Parameters ---------- fitpars : list of lists Parameters for fit ready for fitter! fiterrors : array of numpy vectors Error array for the fitting initialized to '0' for each param parinfo : array of arrays Flags to be used in fit specfile : str Name of the input file containing the spectrum outfilename : str Parameter output filename linecmts : list of lists, optional Reliability flags and comments, e.g., from igmguesses ''' import os ### Set outputs and open files bigfiletowrite = cfg.largeVPparfile filetowrite = outfilename if os.path.isfile(filetowrite): VPparfile = open(filetowrite, 'wb') bigparfile = open(bigfiletowrite, 'ab') # Append to the running list else: VPparfile = open(filetowrite, 'wb') bigparfile = open(bigfiletowrite, 'wb') ### Prep header of line parameter file if linecmts is not None: header = b'specfile|restwave|zsys|col|sigcol|bval|sigbval|vel|sigvel|nflag|bflag|vflag|vlim1|vlim2|wobs1|wobs2|pix1|pix2|z_comp|trans|rely|comment \n' else: header = b'specfile|restwave|zsys|col|sigcol|bval|sigbval|vel|sigvel|nflag|bflag|vflag|vlim1|vlim2|wobs1|wobs2|pix1|pix2|z_comp|trans \n' VPparfile.write(header) bigparfile.write(header) ### Grab parameters/info for each line for i in range(len(fitpars[0])): zline = fitpars[3][i] vlim1 = fitpars[5][i] vlim2 = fitpars[6][i] restwave = fitpars[0][i] wobs1 = restwave * (1 + zline + vlim1 / 299792.458) wobs2 = restwave * (1 + zline + vlim2 / 299792.458) pix1 = jbg.closest(cfg.wave, wobs1) pix2 = jbg.closest(cfg.wave, wobs2) trans = atomicdata.lam2ion(fitpars[0][i]) z_comp = ltu.z_from_dv(fitpars[4][i] * u.km / u.s, zline) if linecmts is not None: towrite = jbg.pipedelimrow([ specfile, restwave, round(zline, 5), round(fitpars[1][i], 3), round(fiterrors[1][i], 3), round(fitpars[2][i], 3), round(fiterrors[2][i], 3), round(fitpars[4][i], 3), round(fiterrors[4][i], 3), parinfo[1][i], parinfo[2][i], parinfo[4][i], vlim1, vlim2, wobs1, wobs2, pix1, pix2, round(z_comp, 5), trans, linecmts[0][i], linecmts[1][i] ]) else: towrite = jbg.pipedelimrow([ specfile, restwave, round(zline, 5), round(fitpars[1][i], 3), round(fiterrors[1][i], 3), round(fitpars[2][i], 3), round(fiterrors[2][i], 3), round(fitpars[4][i], 3), round(fiterrors[4][i], 3), parinfo[1][i], parinfo[2][i], parinfo[4][i], vlim1, vlim2, wobs1, wobs2, pix1, pix2, round(z_comp, 5), trans ]) VPparfile.write(towrite.encode()) bigparfile.write(towrite.encode()) VPparfile.close() bigparfile.close() print('Line parameters written to:') print(filetowrite)
def writelinepars(fitpars,fiterrors,parinfo, specfile, outfilename, linecmts=None): ''' Write fit parameters out to file. Parameters ---------- fitpars : list of lists Parameters for fit ready for fitter! fiterrors : array of numpy vectors Error array for the fitting initialized to '0' for each param parinfo : array of arrays Flags to be used in fit specfile : str Name of the input file containing the spectrum outfilename : str Parameter output filename linecmts : list of lists, optional Reliability flags and comments, e.g., from igmguesses ''' import os ### Set outputs and open files bigfiletowrite = cfg.largeVPparfile filetowrite = outfilename if os.path.isfile(filetowrite): VPparfile = open(filetowrite, 'wb') bigparfile = open(bigfiletowrite, 'ab') # Append to the running list else: VPparfile = open(filetowrite, 'wb') bigparfile = open(bigfiletowrite, 'wb') ### Prep header of line parameter file if linecmts is not None: header = 'specfile|restwave|zsys|col|sigcol|bval|sigbval|vel|sigvel|nflag|bflag|vflag|vlim1|vlim2|wobs1|wobs2|pix1|pix2|z_comp|trans|rely|comment \n' else: header = 'specfile|restwave|zsys|col|sigcol|bval|sigbval|vel|sigvel|nflag|bflag|vflag|vlim1|vlim2|wobs1|wobs2|pix1|pix2|z_comp|trans \n' VPparfile.write(header) bigparfile.write(header) ### Grab parameters/info for each line for i in range(len(fitpars[0])): zline = fitpars[3][i] vlim1 = fitpars[5][i] vlim2 = fitpars[6][i] restwave = fitpars[0][i] wobs1 = restwave * (1 + zline + vlim1 / 299792.458) wobs2 = restwave * (1 + zline + vlim2 / 299792.458) pix1 = jbg.closest(cfg.wave, wobs1) pix2 = jbg.closest(cfg.wave, wobs2) trans = atomicdata.lam2ion(fitpars[0][i]) z_comp = ltu.z_from_dv(fitpars[4][i]*u.km/u.s, zline) if linecmts is not None: towrite = jbg.pipedelimrow( [specfile, restwave, round(zline, 5), round(fitpars[1][i], 3), round(fiterrors[1][i], 3), round(fitpars[2][i], 3), round(fiterrors[2][i], 3), round(fitpars[4][i], 3), round(fiterrors[4][i], 3), parinfo[1][i], parinfo[2][i], parinfo[4][i], vlim1, vlim2, wobs1, wobs2, pix1, pix2,round(z_comp, 5), trans, linecmts[0][i],linecmts[1][i]]) else: towrite = jbg.pipedelimrow( [specfile, restwave, round(zline, 5), round(fitpars[1][i], 3), round(fiterrors[1][i], 3), round(fitpars[2][i], 3), round(fiterrors[2][i], 3), round(fitpars[4][i], 3), round(fiterrors[4][i], 3), parinfo[1][i], parinfo[2][i], parinfo[4][i], vlim1, vlim2, wobs1, wobs2, pix1, pix2, round(z_comp, 5),trans]) VPparfile.write(towrite) bigparfile.write(towrite) VPparfile.close() bigparfile.close() print 'Line parameters written to:' print filetowrite
def lls_stat(LLSs, qsos, vprox=3000. * u.km / u.s, maxdz=99.99, zem_min=0., NHI_cut=17.5, flg_zsrch=0, dz_toler=0.04, LLS_CUT=None, partial=False, prox=False): """ Identify the statistical LLS in a survey Parameters ---------- vprox maxdz zem_min NHI_cut flg_zsrch dz_toler partial : bool, optional Analyze partial LLS? [pLLS] prox : bool, optional Proximate LLS? [PLLS] Returns ------- msk_smpl : bool array True = statistical """ # Search redshift if flg_zsrch == 0: zsrch = qsos['ZT2'] elif flg_zsrch == 1: zsrch = qsos['ZT1'] elif flg_zsrch == 2: zsrch = qsos['ZT0'] # Modify by LLS along QSO sightline as required if LLS_CUT is not None: pdb.set_trace() #zsrch = zsrch > qsos.zlls[LLS_CUT] # LLS msk_smpl = LLSs.zem != LLSs.zem zmax = ltu.z_from_dv( vprox * np.ones(len(qsos)), qsos['ZEM'].data ) # vprox must be array-like to be applied to each individual qsos['ZEM'] # Make some lists lls_coord = LLSs.coord lls_zem = LLSs.zem lls_zabs = LLSs.zabs qsos_coord = SkyCoord(ra=qsos['RA'] * u.deg, dec=qsos['DEC'] * u.deg) for qq, ills in enumerate(LLSs._abs_sys): # Two LLS on one sightline? small_sep = ills.coord.separation(lls_coord) < 3.6 * u.arcsec close_zem = np.abs(ills.zem - lls_zem) < 0.03 close_zabs = np.abs(ills.zabs - lls_zabs) < dz_toler if np.sum(small_sep & close_zem & close_zabs) != 1: raise ValueError("LLS are probably too close in z") # Cut on NHI if partial & (ills.NHI > NHI_cut): continue if ~partial & (ills.NHI <= NHI_cut): continue # Match to QSO RA, DEC idx = np.where((ills.coord.separation(qsos_coord) < 3.6 * u.arcsec) & (np.abs(qsos['ZEM'] - ills.zem) < 0.03))[0] if len(idx) != 1: raise ValueError("Problem with matches") # Query redshift if ((zsrch[idx] > 0.) & (ills.zabs > max(zsrch[idx], qsos['ZEM'][idx] - maxdz) - 1e-4) & (qsos['ZEM'][idx] > zem_min)): if (~prox) & (ills.zabs < zmax[idx]): # Intervening msk_smpl[qq] = True if prox & (ills.zabs >= zmax[idx]): # Proximate msk_smpl[qq] = True # Return return msk_smpl
def load_SDSS_DR7(cls, sample='stat'): """ Load the LLS from the SDSS-DR7 survey (Prochaska+10, ApJ, 718, 391) Parameters ---------- sample : str, optional LLS sample stat : Statistical sample all : All LLS nonstat : Non-statistical sample Returns ------- lls_survey : IGMSurvey """ # LLS File lls_fil = pyigm_path + '/data/LLS/SDSS/lls_dr7_stat_LLS.fits.gz' print('SDSS-DR7: Loading LLS file {:s}'.format(lls_fil)) lls = Table.read(lls_fil) # Rename some columns? lls.rename_column('QSO_RA', 'RA') lls.rename_column('QSO_DEC', 'DEC') # Instantiate scoords = [ lls['RA'][ii] + ' ' + lls['DEC'][ii] for ii in range(len(lls)) ] coords = SkyCoord(scoords, unit=(u.hourangle, u.deg)) lls_survey = cls.from_sfits(lls, coords=coords) lls_survey.ref = 'SDSS-DR7' # QSOs file qsos_fil = pyigm_path + '/data/LLS/SDSS/lls_dr7_qsos_sn2050.fits.gz' print('SDSS-DR7: Loading QSOs file {:s}'.format(qsos_fil)) qsos = Table.read(qsos_fil) lls_survey.sightlines = qsos # All? if sample == 'all': return lls_survey # Stat # z_em cut zem_min = 3.6 lowz_q = qsos['ZEM'] < zem_min qsos['ZT2'][lowz_q] = 99.99 dz_start = 0.4 # Survey path # Using tau=2 lls_survey.sightlines['Z_START'] = np.maximum(qsos['ZT2'], qsos['ZLLS']) # Maximum search lls_survey.sightlines['Z_START'] = np.maximum(qsos['Z_START'], qsos['ZEM'] - dz_start) # Trim bad ones bad_s = np.any([ lls_survey.sightlines['Z_START'] <= 0., lls_survey.sightlines['FLG_QSO'] != 0 ], axis=0) lls_survey.sightlines['Z_START'][bad_s] = 99.99 # zend zend = ltu.z_from_dv(-3000 * u.km / u.s * np.ones(len(qsos)), lls_survey.sightlines['ZEM']) lls_survey.sightlines['Z_END'] = zend # Generate mask print('SDSS-DR7: Performing stats') mask = lls_stat(lls_survey) if sample == 'stat': lls_survey.mask = mask else: lls_survey.mask = ~mask # Return print('SDSS-DR7: Loaded') return lls_survey
def read_joebvp_to_components(filename, coord, llist=None, specfile=None, chk_vel=False): """ Generate a list of AbsComponent objects from a JoeB VP output file Parameters ---------- filename : str joeB VP filename coord : SkyCoord QSO sightline llist : LineList, optional Used to construct AbsLine objects specfile : str, optional chk_vel : bool, optional Demand that the velocities of a given ion all be the same Returns ------- comps : list list of AbsComponent objects """ # init if llist is None: llist = LineList('ISM') comps = [] # Read vp_data = Table.read(filename, format='ascii') # Subset by zsys + trans lbls = [] for izsys, itrans in zip(vp_data['zsys'], vp_data['trans']): lbls.append('{:.6f}_{:s}'.format(izsys, itrans)) lbls = np.array(lbls) ulbls = np.unique(lbls) # Subset by nflag; Build components for lbl in ulbls: mt_lines = np.where(lbls == lbl)[0] if chk_vel: if len(np.unique(vp_data['vel'][mt_lines])) != 1: pdb.set_trace() z_fit = ltu.z_from_dv(vp_data['vel'][mt_lines[0]] * u.km / u.s, vp_data['zsys'][mt_lines[0]]) # Loop on abs lines alines = [] for idx in mt_lines: zlim = [ vp_data['zsys'][idx] + vp_data[vkey][idx] * (1 + vp_data['zsys'][idx]) / ckms for vkey in ['vlim1', 'vlim2'] ] absline = AbsLine(vp_data['restwave'][idx] * u.AA, z=z_fit, zlim=zlim, linelist=llist) # Add measurements [JB -- Want to capture anything else??] absline.attrib['coord'] = coord absline.attrib['flag_N'] = 1 absline.attrib['logN'] = vp_data['col'][idx] absline.attrib['sig_logN'] = vp_data['sigcol'][idx] absline.attrib['b'] = vp_data['bval'][idx] absline.attrib['sig_b'] = vp_data['sigbval'][idx] absline.attrib['z'] = z_fit absline.attrib['sig_z'] = ltu.dz_from_dv( vp_data['sigvel'][idx] * u.km / u.s, vp_data['z_comp'][idx]) if specfile is None: absline.attrib['specfile'] = vp_data['specfile'][idx] else: absline.attrib['specfile'] = specfile # Fill N, sig_N _, _, = linear_clm(absline.attrib) alines.append(absline) # AbsComponent stars = '*' * alines[0].ion_name.count('*') if 'comment' in vp_data.keys(): comment = vp_data['comment'][mt_lines[0]] else: comment = '' if 'rely' in vp_data.keys(): reliability = vp_data['rely'][mt_lines[0]] else: reliability = 'none' abscomp = AbsComponent.from_abslines(alines, stars=stars, comment=comment, reliability=reliability) # Add measurements [JB -- Want to capture anything else??] abscomp.attrib = alines[0].attrib.copy() # Remove undesired keys for key in ['EW', 'sig_EW', 'flag_EW', 'N', 'sig_N']: abscomp.attrib.pop(key) # And more required for key in ['flag_N', 'logN', 'sig_logN']: setattr(abscomp, key, abscomp.attrib[key]) # Errors must be in first line! assert abscomp.sig_logN > 0. comps.append(abscomp) # Finish return comps
def __init__(self, radec, Zion, zcomp, vlim, Ej=0./u.cm, A=None, Ntup=None, comment='', name=None, stars=None, reliability='none'): """ Initiator Parameters ---------- radec : tuple or SkyCoord (RA,DEC) in deg or astropy.coordinate.SkyCoord Zion : tuple Atomic number, ion -- (int,int) e.g. (8,1) for OI Note: (-1, -1) is special and is meant for moleculer (e.g. H2) This notation will most likely change in the future. zcomp : float Absorption component redshift vlim : Quantity array Velocity limits of the component w/r to `z` e.g. [-300,300]*u.km/u.s A : int, optional Atomic mass -- used to distinguish isotopes Ntup : tuple (int,float,two-element list,tuple or array) (flag_N, logN, sig_logN) flag_N : Flag describing N measurement (0: no info; 1: detection; 2: saturated; 3: non-detection) logN : log10 N column density sig_logN : Error in log10 N. Two elements are expected but not required Ej : Quantity, optional Energy of lower level (1/cm) stars : str, optional asterisks to add to name, e.g. '**' for CI** Required if name=None and Ej>0. reliability : str, optional Reliability of AbsComponent 'a' - reliable 'b' - possible 'c' - uncertain 'none' - not defined (default) comment : str, optional A comment, default is `` """ # Required self.coord = ltu.radec_to_coord(radec) self.Zion = Zion # Limits zlim = ltu.z_from_dv(vlim, zcomp) self.limits = zLimits(zcomp, zlim.tolist()) # Attributes self.attrib = init_attrib.copy() # Optional self.A = A self.Ej = Ej self.stars = stars self.comment = comment if Ntup is not None: self.attrib['flag_N'] = Ntup[0] self.attrib['logN'] = Ntup[1] if isiterable(Ntup[2]): self.attrib['sig_logN'] = np.array(Ntup[2]) else: self.attrib['sig_logN'] = np.array([Ntup[2]]*2) _, _ = ltaa.linear_clm(self.attrib) # Set linear quantities # Name if (name is None) and (self.Zion != (-1, -1)): iname = ions.ion_to_name(self.Zion, nspace=0) if self.Ej.value > 0: # Need to put *'s in name if stars is not None: iname += stars else: warnings.warn("No stars provided. Adding one because Ej > 0.") iname += '*' self.name = '{:s}_z{:0.5f}'.format(iname, self.zcomp) elif (name is None) and (self.Zion == (-1, -1)): self.name = 'mol_z{:0.5f}'.format(self.zcomp) else: self.name = name # reliability if reliability not in ['a', 'b', 'c', 'none']: raise ValueError("Input reliability `{}` not valid.".format(reliability)) self.reliability = reliability # AbsLines self._abslines = []
def __init__(self, radec, zabs, vlim, zem=0., abs_type=None, NHI=0., sig_NHI=np.zeros(2), flag_NHI=None, name=None, **kwargs): self.zem = zem # Limits zlim = ltu.z_from_dv(vlim, zabs) self.limits = zLimits(zabs, zlim.tolist()) # NHI self.NHI = NHI self.sig_NHI = sig_NHI # Special handling for the flag as this is often not input with NHI if flag_NHI is None: if NHI > 0.: self.flag_NHI = 1 else: self.flag_NHI = 0 else: self.flag_NHI = flag_NHI self.coord = ltu.radec_to_coord(radec) if name is None: self.name = 'J{:s}{:s}_z{:.6f}'.format( # Should be unique self.coord.icrs.ra.to_string(unit=u.hour, sep='', pad=True), self.coord.icrs.dec.to_string(sep='', pad=True, alwayssign=True), self.zabs) else: self.name = name # Abs type if abs_type is None: self.abs_type = 'NONE' else: self.abs_type = abs_type # Components self._components = [] # List of AbsComponent objects # Kinematics self.kin = {} # Metallicity self.ZH = 0. self.sig_ZH = 0. self.flag_ZH = 0 # Abundances and Tables self._EW = Table() self._ionN = None # Needs to be None for fill_ion self._trans = Table() self._ionstate = {} self._abund = Table() # Refs (list of references) self.Refs = []