from __future__ import print_function, absolute_import, division, unicode_literals from .. import joebgoodies as jbg import numpy as np from linetools.spectralline import AbsLine from linetools.lists import parse as lilp from linetools.lists.linelist import LineList import astropy.units as u import imp ilist = LineList('ISM') jbvp_path = imp.find_module('joebvp')[1] vernerlist = np.genfromtxt(jbvp_path + '/atomicdata/verner6.txt', dtype=None, encoding=None, delimiter=[10, 8, 3, 4, 3, 2, 9, 6]) vernlam = jbg.arrfromcol(vernerlist, 0) vernion = jbg.arrfromcol(vernerlist, 1) vernzatom = jbg.arrfromcol(vernerlist, 2) vernnume = jbg.arrfromcol(vernerlist, 3) verngl = jbg.arrfromcol(vernerlist, 4) verngu = jbg.arrfromcol(vernerlist, 5) vernosc = jbg.arrfromcol(vernerlist, 6) vernp = jbg.arrfromcol(vernerlist, 7) # A start to using the linetools atomic data framework adata = lilp.parse_morton03() vdata = lilp.parse_verner96()
def test_lines_from_ion(): ism = LineList('ISM') # lines = ism[(6, 2)] assert (1334.5323 in lines['wrest'])
def test_closest(): ism = LineList('ISM') ism.closest = True # line = ism[1250.584 * u.AA] # dict with units np.testing.assert_allclose(line['wrest'], 1250.578 * u.AA, rtol=1e-7)
# Match the target with its properties by using RA and DEC cooords w = numpy.logical_and(targ_coord[0] == prop['RA'], targ_coord[1] == prop['DEC']) data = prop[w] radec = SkyCoord(targ_coord[0] + ':00', targ_coord[1] + ':00', unit=(u.hourangle, u.deg)) # Get the expected sodium transition wavelengths from the target properties line = data['Sod_line'] #Define the wavelength range to search for redshifted Sodium lines wvlim = [line - 30.0, line + 30.0] * u.AA strong = LineList('Strong') #Search for lines transitions = strong.available_transitions(wvlim / (1 + data['z']), n_max_tuple=None, min_strength=0.0) # Transform lines to the redshifted wavelength line1 = transitions['wrest'][0] * (1 + data['z']) line2 = transitions['wrest'][1] * (1 + data['z']) # plot the figure fig = plt.figure() fig.suptitle(name[42:-5] + ' z=' + str(data['z'])) plt.axvline(x=line1, color='k', linestyle='--') plt.axvline(x=line2, color='k', linestyle='--') sp.plot(xlim=(line - 30, line + 30)) fig.savefig(name + '.pdf') # Save a copy of the figure
def load_abskin(self, flg=1, kin_file=None, kin_init_file=None): """ Load the absorption-line kinematic data for COS-Halos (or COS-Dwarfs) Calculate from scratch if needed Parameters ---------- flg: int, optional Flag indicating how to load the data 0 = Load from file 1 = Generate kin_init_file: str Name of kinematics driver file kin_file: str Name of kinematics output file [First made for John Forbes] """ from linetools.lists.linelist import LineList ism = LineList('ISM') if flg == 0: # Load if kin_file is None: kin_file = os.path.abspath( os.environ.get('DROPBOX_DIR') + '/COS-Halos/Kin/' + 'COS-Halos_kin.fits') hdu = fits.open(kin_file) # Metals metals = Table(hdu[1].data) for row in metals: mt = np.where((row['field'] == self.field) & (row['gal_id'] == self.gal_id))[0] pdb.set_trace() elif flg == 1: # Generate # Read init file if kin_init_file is None: kin_init_file = self.kin_init_file kin_init = Table.read(kin_init_file, format='ascii') # Loop to my loop fgal = zip(self.field, self.gal_id) for qq, cgm_abs in enumerate(self.cgm_abs): # Match to kin_init mt = np.where((kin_init['QSO'] == cgm_abs.field) & (kin_init['Galaxy'] == cgm_abs.gal_id))[0] if len(mt) == 0: warnings.warn( 'load_kin: No kinematics for {:s}, {:s}'.format( cgm_abs.field, cgm_abs.gal_id)) continue mt = mt[0] # Metals if kin_init['flgL'][mt] > 0: wrest = kin_init['mtl_wr'][mt] * u.AA if wrest.value <= 1: pdb.set_trace() spec = self.load_bg_cos_spec(qq, wrest) vmnx = (kin_init['L_vmn'][mt], kin_init['L_vmx'][mt]) * u.km / u.s # Process aline = AbsLine(wrest, linelist=ism) aline.analy['spec'] = spec aline.setz(cgm_abs.igm_sys.zabs) aline.limits.set(vmnx) fx, sig, cdict = aline.cut_spec() # Kin stau = laak.generate_stau(cdict['velo'], fx, sig) cgm_abs.igm_sys.kin['Metal'] = laak.pw97_kin(cdict['velo'], stau, per=0.07) cgm_abs.igm_sys.kin['Metal'].update( laak.cgm_kin(cdict['velo'], stau, per=0.07)) # Save spec #cgm_abs.igm_sys.kin['Metal']['spec'] = spec else: cgm_abs.igm_sys.kin['Metal'] = {} # HI if kin_init['flgH'][mt] > 0: wrest = kin_init['HI_wrest'][mt] * u.AA if wrest.value <= 1: pdb.set_trace() spec = self.load_bg_cos_spec(qq, wrest) if spec is None: pdb.set_trace() vmnx = (kin_init['HIvmn'][mt], kin_init['HIvmx'][mt]) * u.km / u.s # Process aline = AbsLine(wrest, linelist=ism) aline.analy['spec'] = spec aline.setz(cgm_abs.igm_sys.zabs) aline.limits.set(vmnx) fx, sig, cdict = aline.cut_spec() # Kin stau = laak.generate_stau(cdict['velo'], fx, sig) cgm_abs.igm_sys.kin['HI'] = laak.pw97_kin(cdict['velo'], stau, per=0.07) cgm_abs.igm_sys.kin['HI'].update( laak.cgm_kin(cdict['velo'], stau, per=0.07)) else: # Fill with zeros (for the keys) cgm_abs.igm_sys.kin['HI'] = {}
def add_abslines_from_linelist(self, llist='ISM', init_name=None, wvlim=None, min_Wr=None, **kwargs): """ It adds associated AbsLines satisfying some conditions (see parameters below). Parameters ---------- llist : str, optional Name of the linetools.lists.linelist.LineList object where to look for the transition names. Default is 'ISM', which means the function looks within `list = LineList('ISM')`. init_name : str, optional Name of the initial transition used to define the AbsComponent wvlim : Quantity array, optional Observed wavelength limits for AbsLines to be added. e.g. [1200, 2000]*u.AA. min_Wr : Quantity, optional Minimum rest-frame equivalent with for AbsLines to be added. This is calculated in the very low optical depth regime tau0<<1, where Wr is independent of Doppler parameter or gamma (see eq. 9.15 of Draine 2011). Still, a column density attribute for the AbsComponent is needed. Returns ------- Adds AbsLine objects to the AbsComponent._abslines list. Notes ----- **kwargs are passed to AbsLine.add_absline() method. """ # get the transitions from LineList llist = LineList(llist) if init_name is None: # we have to guess it if (self.Zion) == (-1, -1): # molecules # init_name must be in self.attrib (this is a patch) init_name = self.attrib['init_name'] else: # atoms init_name = ions.ion_to_name(self.Zion, nspace=0) transitions = llist.all_transitions(init_name) # unify output to be a Table if isinstance(transitions, dict): transitions = llist.from_dict_to_table(transitions) # check wvlims if wvlim is not None: # Deal with units wrest = transitions['wrest'].data * transitions['wrest'].unit # Logic cond = (wrest*(1+self.zcomp) >= wvlim[0]) & \ (wrest*(1+self.zcomp) <= wvlim[1]) transitions = transitions[cond] # check outputs if len(transitions) == 0: warnings.warn( "No transitions satisfying the criteria found. Doing nothing.") return # loop over the transitions when more than one found for transition in transitions: iline = AbsLine(transition['name'], z=self.zcomp, linelist=llist) iline.limits.set(self.vlim) iline.attrib['coord'] = self.coord iline.attrib['logN'] = self.logN iline.attrib['sig_logN'] = self.sig_logN iline.attrib['flag_N'] = self.flag_N iline.attrib['N'] = 10**iline.attrib['logN'] / (u.cm * u.cm) iline.attrib['sig_N'] = 10**iline.attrib['sig_logN'] / (u.cm * u.cm) for key in self.attrib.keys(): iline.attrib[key] = self.attrib[key] if min_Wr is not None: # check logN is defined logN = self.logN if logN == 0: warnings.warn( "AbsComponent does not have logN defined. Appending AbsLines " "regardless of min_Wr.") else: N = 10**logN / (u.cm * u.cm) Wr_iline = iline.get_Wr_from_N( N=N) # valid for the tau0<<1 regime. if Wr_iline < min_Wr: # do not append continue # add the absline self.add_absline(iline)
def get_telfer_spec(zqso=0., igm=False, fN_gamma=None, LL_flatten=True, nproc=6, nskip=10): """Generate a Telfer QSO composite spectrum Parameters ---------- zqso : float, optional Redshift of the QSO igm : bool, optional Include IGM opacity? [False] fN_gamma : float, optional Power-law evolution in f(N,X) LL_flatten : bool, optional Set Telfer to a constant below the LL? nproc : int, optional For multi-processing with IGM nskip : int, optional Evaluate IGM every nskip Returns ------- telfer_spec : XSpectrum1D Spectrum """ # Read telfer = ascii.read(pyigm_path + '/data/quasar/telfer_hst_comp01_rq.ascii', comment='#') scale = telfer['flux'][(telfer['wrest'] == 1450.)] telfer_spec = XSpectrum1D.from_tuple( (np.array(telfer['wrest']) * (1 + zqso), np.array(telfer['flux']) / scale[0])) # Observer frame # IGM? if igm is True: """The following concept is rather experimental. Use at your own risk. """ import multiprocessing fN_model = FNModel.default_model() # Expanding range of zmnx (risky) fN_model.zmnx = (0., 5.5) if fN_gamma is not None: fN_model.gamma = fN_gamma # Setup inputs #EW_FIL = pyigm_path+'/data/fN/EW_SPLINE_b24.yml' #with open(EW_FIL, 'r') as infile: # EW_spline = yaml.load(infile) # dict from mk_ew_lyman_spline HI = LineList('HI') twrest = u.Quantity(HI._data['wrest']) # Parallel if LL_flatten: igm_wv = np.where((telfer['wrest'] > 900.) & (telfer['wrest'] < 1220.))[0] else: igm_wv = np.where(telfer['wrest'] < 1220.)[0] adict = [] sub_wv = igm_wv[0::nskip] if (len(igm_wv) % nskip) != 0: # Make sure the last element is present sub_wv = np.concatenate([sub_wv, np.array([igm_wv[-1]])]) for wrest in telfer_spec.wavelength[sub_wv].value: tdict = dict(ilambda=wrest, zem=zqso, fN_model=fN_model, wrest=twrest.copy()) adict.append(tdict) # Run if nproc > 1: pool = multiprocessing.Pool( nproc) # initialize thread pool N threads ateff = pool.map(pyift.map_lymanew, adict) else: ateff = map(pyift.map_lymanew, adict) # Interpolate all_val = interp1d(telfer_spec.wavelength[sub_wv].value, np.array(ateff))(telfer_spec.wavelength[igm_wv]) # Apply new_flux = telfer_spec.flux.value new_flux[igm_wv] *= np.exp(-1. * np.array(all_val)) # Flatten? if LL_flatten: wv_LL = np.where( np.abs(telfer_spec.wavelength / (1 + zqso) - 914. * u.AA) < 3. * u.AA)[0] f_LL = np.median(new_flux[wv_LL]) wv_low = np.where(telfer_spec.wavelength / (1 + zqso) < 911.7 * u.AA)[0] new_flux[wv_low] = f_LL # Regenerate spectrum telfer_spec = XSpectrum1D.from_tuple( (np.array(telfer['wrest']) * (1 + zqso), new_flux)) # Return return telfer_spec
def __init__(self, ispec, guessfile=None, parent=None, zsys=None, norm=None, exten=None, rsp_kwargs={}, unit_test=False, screen_scale=1., **kwargs): QMainWindow.__init__(self, parent) """ ispec = str, XSpectrum1D or tuple of arrays Input spectrum or spectrum filename. If tuple then (wave, fx), (wave, fx, sig) or (wave, fx, sig, co) guessfile : str, optional name of the .json file generated with igmguesses GUI in Pyigm (see https://github.com/pyigm/pyigm/blob/master/docs/igmguesses.rst) if not None - overplot fitted line profiles from igmguesses parent : Widget parent, optional zsys : float, optional intial redshift exten : int, optional extension for the spectrum in multi-extension FITS file norm : bool, optional True if the spectrum is normalized screen_scale : float, optional Scale the default sizes for the gui size """ #reload(ltgl) #reload(ltgsp) # INIT #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() self.scale = screen_scale # Needed to avoid crash in large spectral files rcParams['agg.path.chunksize'] = 20000 rcParams[ 'axes.formatter.useoffset'] = False # avoid scientific notation in axes tick labels # Build a widget combining several others self.main_widget = QWidget() # Status bar self.create_status_bar() # Grab the pieces and tie together self.pltline_widg = ltgl.PlotLinesWidget(status=self.statusBar, init_z=zsys, screen_scale=self.scale) self.pltline_widg.setMaximumWidth(300 * self.scale) ## Abs sys abs_sys = None voigtsfit = None if guessfile is not None: # Load ism = LineList('ISM') igm_guess = ltu.loadjson(guessfile) comps = [] for key in igm_guess['cmps'].keys(): comp = AbsComponent.from_dict(igm_guess['cmps'][key], chk_vel=False, linelist=ism) comps.append(comp) abs_sys = ltiu.build_systems_from_components( comps, vsys=500. * u.km / u.s) # ,chk_z=False) ### 100000.*u.km/u.s ok ### voigt fit - added # Spectrum spec, spec_fil = ltgu.read_spec(ispec, exten=exten, norm=norm, rsp_kwargs=rsp_kwargs) voigtsfit = np.asarray([0] * len(spec.wavelength)) alllines = [] for iabs_sys in abs_sys: lines = iabs_sys.list_of_abslines() alllines = alllines + lines if len(alllines) > 0: voigtsfit = lav.voigt_from_abslines(spec.wavelength, alllines, fwhm=3.).flux.value if not norm: voigtsfit = voigtsfit * spec.co # Hook the spec widget to Plot Line self.spec_widg = ltgsp.ExamineSpecWidget(ispec, guessfile=guessfile, voigtsfit=voigtsfit, status=self.statusBar, parent=self, llist=self.pltline_widg.llist, zsys=zsys, norm=norm, exten=exten, abs_sys=abs_sys, screen_scale=self.scale, rsp_kwargs=rsp_kwargs, **kwargs) # Reset redshift from spec if zsys is None: if hasattr(self.spec_widg.spec, 'z'): self.pltline_widg.setz( str(self.spec_widg.spec.z[self.spec_widg.select])) # Auto set line list if spec has proper object type if hasattr(self.spec_widg.spec, 'stypes'): if self.spec_widg.spec.stypes[ self.spec_widg.select].lower() == 'galaxy': self.pltline_widg.llist = ltgu.set_llist( 'Galaxy', in_dict=self.pltline_widg.llist) elif self.spec_widg.spec.stypes[ self.spec_widg.select].lower() == 'absorber': self.pltline_widg.llist = ltgu.set_llist( 'Strong', in_dict=self.pltline_widg.llist) self.pltline_widg.llist['Plot'] = True idx = self.pltline_widg.lists.index( self.pltline_widg.llist['List']) self.pltline_widg.llist_widget.setCurrentRow(idx) # self.pltline_widg.spec_widg = self.spec_widg # Multi spec self.mspec_widg = ltgsp.MultiSpecWidget(self.spec_widg) self.spec_widg.canvas.mpl_connect('button_press_event', self.on_click) # Layout # Extras extras = QWidget() extras.setMinimumWidth(180 * self.scale) extras.setMaximumWidth(280 * self.scale) vbox = QVBoxLayout() qbtn = QPushButton(self) qbtn.setText('Quit') qbtn.clicked.connect(self.quit) vbox.addWidget(self.pltline_widg) vbox.addWidget(self.mspec_widg) vbox.addWidget(qbtn) extras.setLayout(vbox) # Main window hbox = QHBoxLayout() hbox.addWidget(self.spec_widg) hbox.addWidget(extras) self.main_widget.setLayout(hbox) # Point MainWindow self.setCentralWidget(self.main_widget) if unit_test: self.quit()
def fill_data(self, trans, linelist=None, closest=False, verbose=True, use_CACHE=False, **kwargs): """ Fill atomic data and setup analy. Parameters ---------- trans : Quantity or str Either a rest wavelength (e.g. 1215.6700*u.AA) or the name of a transition (e.g. 'CIV 1548'). For an unknown transition use string 'unknown'. linelist : str or LineList or list of LineList objects, optional Class of linelist or str setting LineList or list of LineList objects closest : bool, optional Take the closest line to input wavelength? [False] """ global CACHE_LLIST # Only cached if LineList is *not* input # Deal with LineList flg_list = False if linelist is None: if use_CACHE and (CACHE_LLIST is not None): llist = CACHE_LLIST else: if self.ltype == 'Abs': llist = LineList('ISM') elif self.ltype == 'Em': llist = LineList('Galaxy') else: raise ValueError("Not ready for ltype = {:s}".format( self.ltype)) elif isinstance(linelist, basestring): llist = LineList(linelist) elif isinstance(linelist, LineList): llist = linelist elif isinstance(linelist, list): llist = linelist[0] flg_list = True else: raise ValueError('Bad input for linelist') # Cache CACHE_LLIST = llist # Closest? llist.closest = closest # Data if flg_list: # Allow for a list of LineList for llist in linelist: llist.closest = closest newline = llist[trans] if newline is not None: break else: newline = llist[trans] # Success? if newline is None: print("Transition {} not found in LineList {:s}".format( trans, llist.list)) raise ValueError("You may need to set clear_CACHE_LLIST=True") try: self.data.update(newline) # Expected to be a LineList dict object except TypeError: raise TypeError("Probably should not be here") # Update if isinstance(self.data, dict): self.wrest = self.data['wrest'] else: self.wrest = self.data['wrest'] * self.data['wrest'].unit self.name = self.data['name'] # self.update() # is this used ?
def jenkins2005(): """Jenkins, E. et al. 2005, ApJ, 2005, 623, 767 PHL 1811 HST/STIS, FUSE Metals parsed from Table 1 OI taken from text Had to input error on columns by hand (JXP) Total NHI from Lyman series. see Fig 3 M/H from O/H """ # Grab ASCII file from ApJ tab_fil = pyigm_path+"/data/LLS/Literature/jenkins2005.tb1.ascii" chk_fil = glob.glob(tab_fil) if len(chk_fil) > 0: tab_fil = chk_fil[0] else: url = 'http://iopscience.iop.org/0004-637X/623/2/767/fulltext/61520.tb1.txt' print('LLSSurvey: Grabbing table file from {:s}'.format(url)) f = urllib2.urlopen(url) with open(tab_fil, "wb") as code: code.write(f.read()) # Setup radec = '215501.5152-092224.688' # SIMBAD lls = LLSSystem(name='PHL1811_z0.081', radec=radec, zem=0.192, zabs=0.080923, vlim=[-100., 100.]*u.km/u.s, NHI=17.98, ZH=-0.19, sig_NHI=np.array([0.05,0.05])) lls.lines = [] # Probably not used # AbsLines ism = LineList('ISM') Nsig = {'C IV': 0.4, 'N II': 0.4, 'Si II': 0.05, 'Si IV': 0.25, 'S II': 0.2, 'Fe II': 0.12, 'H I': 0.05, 'S III': 0.06} # Parse Table with open(tab_fil,'r') as f: flines = f.readlines() ion_dict = {} for iline in flines: iline = iline.strip() if (len(iline) == 0): continue # Split on tabs isplit = iline.split('\t') # Offset? ioff = 0 if isplit[0][0] in ['1','2']: ioff = -1 # Catch bad lines if (isplit[1+ioff][0:6] in ['1442.0','1443.7','1120.9']): # Skip goofy CII line and CII* continue if len(isplit[2+ioff]) == 0: continue # Ion if (len(isplit[0].strip()) > 0) & (isplit[0][0] not in ['1','2']): ionc = isplit[0].strip() try: Zion = ltai.name_ion(ionc) except KeyError: pdb.set_trace() # Generate the Line try: newline = AbsLine(float(isplit[2+ioff])*u.AA,linelist=ism, closest=True) except ValueError: pdb.set_trace() newline.attrib['z'] = lls.zabs # Spectrum newline.analy['datafile'] = 'STIS' if 'S' in isplit[1] else 'FUSE' # EW try: EWvals = isplit[4+ioff].split(' ') except IndexError: pdb.set_trace() newline.attrib['EW'] = float(EWvals[0])*u.AA/1e3 newline.attrib['sig_EW'] = float(EWvals[2])*u.AA/1e3 newline.attrib['flag_EW'] = 1 if len(isplit) < (5+ioff+1): continue # Colm? #xdb.set_trace() newline.attrib['sig_logN'] = 0. if (len(isplit[5+ioff].strip()) > 0) & (isplit[5+ioff].strip() != '\\ldots'): if isplit[5+ioff][0] == '\\': ipos = isplit[5+ioff].find(' ') newline.attrib['logN'] = float(isplit[5+ioff][ipos+1:]) newline.attrib['flag_N'] = 2 elif isplit[5+ioff][0] == '<': ipos = 0 newline.attrib['logN'] = float(isplit[5+ioff][ipos+1:]) newline.attrib['flag_N'] = 3 elif isplit[5+ioff][0] == '1': try: newline.attrib['logN'] = float(isplit[5+ioff][0:5]) except ValueError: pdb.set_trace() newline.attrib['flag_N'] = 1 try: newline.attrib['sig_logN'] = Nsig[ionc] except KeyError: print('No error for {:s}'.format(ionc)) else: raise ValueError('Bad character') # ion_dict ion_dict[ionc] = dict(clm=newline.attrib['logN'], sig_clm=newline.attrib['sig_logN'], flg_clm=newline.attrib['flag_N'], Z=Zion[0], ion=Zion[1]) # Append lls.lines.append(newline) # Fix NI, OI ion_dict['O I']['clm'] = 14.47 ion_dict['O I']['sig_clm'] = 0.05 ion_dict['N I']['flg_clm'] = 3 lls._ionN = pyiau.dict_to_ions(ion_dict) lls.Refs.append('Jen05') # Return return lls
def fig_molecules(): """ H2, Lya, CII plots """ # Spec files # Load QPQ7 #if qpq7 is None: # qpq7 = equ.load_qpq(7) # LineList H2 = LineList('H2') CO_waves = [1447.3521, 1477.5649, 1509.7480] CO_lbls = ['CO 2-0', 'CO 1-0', 'CO 0-0'] outfil = 'fig_qpq_molecules.pdf' pp = PdfPages(outfil) fig = plt.figure(figsize=(8.5, 4)) plt.clf() gs = gridspec.GridSpec(2, 5) xpsimp.dark_bkgd(plt) qpq8_sys = ['J114436.65+095904.9', 'J142758.73-012136.1'] qpq8_yrng = [(0., 1.2), (0., 1.2)] wvobs = [(4160, 4240.), (4735, 4950)] # Lyman-Werner, CO #qpq8_sys = ['J120416.68+022110.9'] qpq8_zfg = [2.973, 2.27616] trans = [1215.6701, 1334.5323] tvmnx = [(-400, 400), (-200, 200)] lbls = ['HI Lya', 'CII 1334'] for qq, qsys in enumerate(qpq8_sys): # Load spectrum sdict = eqs.spec_wvobs(qsys, wvobs[qq][0] * u.AA, high_res=2) spec = sdict['spec'] spec.normalize(sdict['conti']) if qq == 1: # Smooth for presentation tmpspec = XSpectrum1D.from_tuple((spec.wavelength, spec.flux)) spec = tmpspec.box_smooth(3) # Loop on standard transitions for jj, itrans in enumerate(trans): ax = plt.subplot(gs[qq, jj]) ax.set_xlim(tvmnx[jj]) ax.xaxis.set_major_locator(plt.MultipleLocator(200.)) ax.set_ylim(-0.05, 1.2) if jj == 0: ax.set_ylabel('Flux') ax.set_xlabel('Velocity (km/s)') # Plot velo = spec.relative_vel(itrans * u.AA * (1 + qpq8_zfg[qq])) ax.plot(velo, spec.flux, color='white', drawstyle='steps') ax.plot(tvmnx[jj], [1.] * 2, '--', color='lightgreen') # Label ax.text(0.50, 0.90, lbls[jj], transform=ax.transAxes, size='large', ha='center', color='yellow') # Molecules ax = plt.subplot(gs[qq, len(trans):]) ax.set_xlim(wvobs[qq]) ax.set_ylim(qpq8_yrng[qq]) ax.set_xlabel('Wavelength (Angstroms)') ax.plot(spec.wavelength, spec.flux, color='white', drawstyle='steps') if qq == 0: # Lyman-Werner wrlim = [iwv * u.AA / (1 + qpq8_zfg[qq]) for iwv in wvobs[qq]] gdlin = np.where((H2._fulltable['Jk'] < 7) & (H2._fulltable['wrest'] > wrlim[0]) & (H2._fulltable['wrest'] < wrlim[1]))[0] for igd in gdlin: if H2._fulltable['Jk'][igd] <= 2: clr = 'cyan' do_lbl = True else: clr = 'gray' do_lbl = False ax.plot( [H2._fulltable['wrest'][igd].value * (1 + qpq8_zfg[qq])] * 2, qpq8_yrng[qq], '--', color=clr) if do_lbl: ax.text(H2._fulltable['wrest'][igd].value * (1 + qpq8_zfg[qq]), 0.4, H2._fulltable['name'][igd], rotation=90., color=clr, size=12) else: # CO for jj, CO_wave in enumerate(CO_waves): ax.plot([CO_wave * (1 + qpq8_zfg[qq])] * 2, qpq8_yrng[qq], '--', color='cyan') ax.text(CO_wave * (1 + qpq8_zfg[qq]), 0.4, CO_lbls[jj], rotation=90, color='cyan', size=12) # Write plt.tight_layout(pad=0.2, h_pad=0., w_pad=0.1) pp.savefig() pp.close() plt.close() print('Genereated {:s}'.format(outfil))
def set_llist(llist, in_dict=None, sort=True): ''' Method to set a line list dict for the Widgets sort: bool, optional [DEPRECATED CURRENTLY] Sort lines by rest wavelength [True] ''' from linetools.lists.linelist import LineList from astropy.units.quantity import Quantity if in_dict is None: in_dict = {} if isinstance(llist, basestring): # Set line list from a file in_dict['List'] = llist if llist == 'None': in_dict['Plot'] = False else: in_dict['Plot'] = True # Load? if not (llist in in_dict): # Homebrew if llist == 'OVI': gdlines = u.AA * [ 629.730, 702.332, 770.409, 780.324, 787.711, 832.927, 972.5367, 977.0201, 1025.7222, 1031.9261, 1037.6167, 1206.5, 1215.6700, 1260.4221 ] llist_cls = LineList('Strong', subset=gdlines) in_dict[llist] = llist_cls else: llist_cls = LineList(llist) # Sort llist_cls._data.sort('wrest') # Load in_dict[llist] = llist_cls elif isinstance(llist, (Quantity, list)): # Set from a list of wrest in_dict['List'] = 'input.lst' in_dict['Plot'] = True # Fill if sort: llist.sort() llist_cls = LineList('ISM', subset=llist) in_dict['input.lst'] = llist_cls ''' line_file = xa_path+'/data/spec_lines/grb.lst' llist_cls = xspec.abs_line.Abs_Line_List(line_file) adict = llist_cls.data # Fill names = [] fval = [] for wrest in llist: mt = np.where(np.abs(wrest-adict['wrest']) < 1e-3)[0] if len(mt) != 1: raise ValueError('Problem!') names.append(adict['name'][mt][0]) fval.append(adict['fval'][mt][0]) # Set #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() # Generate a Table col0 = Column(np.array(llist), name='wrest', unit=u.AA) # Assumed Angstroms col1 = Column(np.array(names), name='name') col2 = Column(np.array(fval), name='fval') in_dict['input.lst'] = Table( (col0,col1,col2) ) ''' else: raise IOError('Not ready for this type of input') # Return return in_dict
def load_coolgas(self): """ Load data on cool gas (CII, CIV, SiII, SiIII) Richter+17 """ llist = LineList('ISM') # Ricther+17 print('Loading Richter+17 for CII, CIV, SiII, SiIII') r17_a1_file = resource_filename('pyigm', '/data/CGM/Galaxy/richter17_A1.fits') r17_a1 = Table.read(r17_a1_file) r17_a2_file = resource_filename('pyigm', '/data/CGM/Galaxy/richter17_A2.fits') r17_a2 = Table.read(r17_a2_file) # Coords coords = SkyCoord(ra=r17_a1['_RAJ2000'], dec=r17_a1['_DEJ2000'], unit='deg') gc = coords.transform_to('galactic') ra = np.zeros((len(r17_a2))) dec = np.zeros((len(r17_a2))) # Loop on Sightlines for kk, row in enumerate(r17_a1): if self.debug and (kk == 5): break a2_idx = np.where(r17_a2['Name'] == row['Name'])[0] if len(a2_idx) == 0: continue ra[a2_idx] = row['_RAJ2000'] dec[a2_idx] = row['_DEJ2000'] # Generate the components icoord = gc[kk] alines = [] for jj, idx in enumerate(a2_idx): # Transition trans = '{:s} {:d}'.format(r17_a2['Ion'][idx].strip(), int(r17_a2['lambda0'][idx])) try: aline = AbsLine(trans, linelist=llist) except ValueError: pdb.set_trace() aline.attrib['coord'] = icoord # Skip EW=0 lines if r17_a2['e_W'][idx] == 0: continue # Velocity z = 0. aline.setz(z) vlim = np.array([r17_a2['vmin'][idx], r17_a2['vmax'][idx] ]) * u.km / u.s aline.limits.set(vlim) # These are v_LSR # EW aline.attrib['flag_EW'] = 1 aline.attrib['EW'] = r17_a2['W'][idx] / 1e3 * u.AA aline.attrib['sig_EW'] = r17_a2['e_W'][idx] / 1e3 * u.AA # Column if np.isnan( r17_a2['logN'] [idx]): # Odd that some lines had an error but no value aline.attrib['flag_N'] = 0 elif r17_a2['l_logN'][idx] == '>': aline.attrib['flag_N'] = 2 aline.attrib['sig_logN'] = 99.99 else: aline.attrib['flag_N'] = 1 aline.attrib['sig_logN'] = r17_a2['e_logN'][idx] aline.attrib['logN'] = r17_a2['logN'][idx] # Fill linear _, _ = linear_clm(aline.attrib) alines.append(aline) # Generate components from abslines comps = ltiu.build_components_from_abslines(alines, chk_sep=False, chk_vel=False) # Limits vmin = np.min([icomp.limits.vmin.value for icomp in comps]) vmax = np.max([icomp.limits.vmax.value for icomp in comps]) # Instantiate s_kwargs = dict(name=row['Name'] + '_z0') c_kwargs = dict(chk_sep=False, chk_z=False) abssys = IGMSystem.from_components(comps, vlim=[vmin, vmax] * u.km / u.s, s_kwargs=s_kwargs, c_kwargs=c_kwargs) # CGM Abs rho, ang_sep = calc_Galactic_rho(abssys.coord) cgmabs = CGMAbsSys(self.galaxy, abssys, rho=rho, ang_sep=ang_sep, cosmo=self.cosmo) # Add to cgm_abs self.abs.cgm_abs.append(cgmabs) # Finish r17_a2['RA'] = ra r17_a2['DEC'] = dec self.richter17 = r17_a2 # Reference if len(self.refs) > 0: self.refs += ',' self.refs += 'Richter+17'
def load_hotgas(self): """ Load data on hot gas (e.g. OVII, OVIII) Fang+15 """ # Init llist = LineList('EUV') ovii = AbsLine('OVII 21', linelist=llist) scoord = self.abs.scoord # Sightline coordiantes # Fang+15 Table 1 [OVII] fang15_file = resource_filename('pyigm', '/data/CGM/Galaxy/fang15_table1.dat') self.fang15 = Table.read(fang15_file, format='cds') print('Loading Fang+15 for OVII') # Reference if len(self.refs) > 0: self.refs += ',' self.refs += 'Fang+15' # Generate the systems # # (should check to see if low-ion ones exist already) for row in self.fang15: # Coordinates gc = SkyCoord(l=row['GLON'] * u.degree, b=row['GLAT'] * u.degree, frame='galactic') # Limits # OVII line aline = ovii.copy() aline.attrib['coord'] = gc z = row['Vel'] / c_kms try: aline.setz(z) except IOError: z = 0. vlim = np.array([-300, 300]) * u.km / u.s aline.attrib['flag_EW'] = 3 aline.attrib['EW'] = row['EW1'] / 1e3 * u.AA aline.attrib['sig_EW'] = 99. * u.AA # aline.attrib[ 'flag_N'] = 0 # Might be able to set an upper limit else: aline.attrib['b'] = row['b'] * u.km / u.s aline.attrib['flag_EW'] = 1 aline.attrib['EW'] = row['EW1'] / 1e3 * u.AA aline.attrib['sig_EW'] = row['e_EW1'] / 1e3 * u.AA vlim = np.array( [-1, 1]) * (2 * row['b'] + 2 * row['E_b']) * u.km / u.s # N_OVII aline.attrib['flag_N'] = 1 aline.attrib['logN'] = row['logNO'] aline.attrib['sig_logN'] = np.array( [row['e_logNO'], row['E_logNO']]) # Fill linear _, _ = linear_clm(aline.attrib) # OVII aline.limits.set(vlim) # Generate component and add comp = AbsComponent.from_abslines([aline]) if aline.attrib['flag_N'] == 0: # Hack to merge later comp.attrib['sig_logN'] = np.array([0., 0.]) else: pass # Check for existing system minsep = np.min(comp.coord.separation(scoord).to('arcsec')) if minsep < 30 * u.arcsec: # Add component to existing system idx = np.argmin(comp.coord.separation(scoord).to('arcsec')) if self.verbose: print("Adding OVII system to {}".format( self.abs.cgm_abs[idx].igm_sys)) self.abs.cgm_abs[idx].igm_sys.add_component(comp, chk_sep=False, debug=True) else: # Instantiate abssys = IGMSystem(gc, z, vlim, name=row['Name'] + '_z0', zem=row['z']) abssys.add_component(comp, chk_sep=False) # CGM Abs rho, ang_sep = calc_Galactic_rho(abssys.coord) cgmabs = CGMAbsSys(self.galaxy, abssys, rho=rho, ang_sep=ang_sep, cosmo=self.cosmo) # Add to cgm_abs self.abs.cgm_abs.append(cgmabs) scoord = self.abs.scoord # Sightline coordiantes # Savage+03 Table 2 [OVI] -- Thick disk/halo only?? print('Loading Savage+03 for OVI') savage03_file = resource_filename( 'pyigm', '/data/CGM/Galaxy/savage03_table2.fits') self.savage03 = Table.read(savage03_file) # Reference if len(self.refs) > 0: self.refs += ',' self.refs += 'Savage+03' # Generate the systems # # (should check to see if low-ion ones exist already) for row in self.savage03: # Coordinates coord = SkyCoord(ra=row['_RA'] * u.deg, dec=row['_DE'] * u.deg, frame='icrs') gc = coord.transform_to('galactic') # Build the component vlim = np.array([row['V-'], row['V_']]) * u.km / u.s comp = AbsComponent(gc, (8, 6), 0., vlim) # Add attributes if row['b'] > 0.: comp.attrib['vcen'] = row['__v_obs'] * u.km / u.s comp.attrib['sig_vcen'] = row['e__v_obs'] * u.km / u.s comp.attrib['b'] = row['b'] * u.km / u.s comp.attrib['sig_b'] = row['e_b'] * u.km / u.s # Column comp.attrib['flag_N'] = 1 comp.attrib['logN'] = row['logN_OVI_'] comp.attrib['sig_logN'] = np.array( [np.sqrt(row['e_sc']**2 + row['e_sys']**2)] * 2) else: # Upper limit comp.attrib['flag_N'] = 3 comp.attrib['logN'] = row['logN_OVI_'] comp.attrib['sig_logN'] = np.array([99.] * 2) # Set linear quantities _, _ = linear_clm(comp.attrib) # Check for existing system minsep = np.min(comp.coord.separation(scoord).to('arcsec')) if minsep < 30 * u.arcsec: idx = np.argmin(comp.coord.separation(scoord).to('arcsec')) self.abs.cgm_abs[idx].igm_sys.add_component(comp, chk_sep=False, debug=True, update_vlim=True) else: # New if row['RV'] > 0: zem = row['RV'] / c_kms else: zem = row['z'] abssys = IGMSystem(gc, comp.zcomp, vlim, name=row['Name'] + '_z0', zem=zem) abssys.add_component(comp, chk_sep=False, debug=True) # CGM Abs rho, ang_sep = calc_Galactic_rho(abssys.coord) cgmabs = CGMAbsSys(self.galaxy, abssys, rho=rho, ang_sep=ang_sep, cosmo=self.cosmo) # Add to cgm_abs self.abs.cgm_abs.append(cgmabs)
def fig_lya_line(lw=1.5, csz=15.): """ Generate a DLA in optical depth and flux space Parameters ---------- """ llist = LineList('ISM') # Lya lya = AbsLine('HI 1215', z=0., llist=llist) # Wavelength wave = np.linspace(1100., 1310., 100000)*u.AA outfile = 'fig_lya_line.png' # Figure plt.figure(figsize=(8, 4)) plt.clf() gs = gridspec.GridSpec(1, 2) # Tau plot ax1 = plt.subplot(gs[0]) # Optical depth lya.attrib['N'] = 1e21 / u.cm**2 lya.attrib['b'] = 20 * u.km/u.s tau = voigt_from_abslines(wave, lya, ret='tau') # Plot ax1.plot(wave, tau, 'g') # Axes ax1.set_xlim(1200., 1230.) ax1.set_ylim(1e-2, 5e7) ax1.set_yscale("log", nonposy='clip') ax1.set_ylabel(r'Optical Depth for $N_{\rm HI} = 10^{21} \, \rm cm^{-2}$') ax1.set_xlabel(r'Wavelength ($\AA$)') ax1.xaxis.set_major_locator(plt.MultipleLocator(10.)) # set_spines(ax1, 2.) set_fontsize(ax1,csz) # Flux space ax2 = plt.subplot(gs[1]) for logN in [19., 20., 21., 22.]: lya.attrib['N'] = 10**logN / u.cm**2 spec = voigt_from_abslines(wave, lya) # Plot ax2.plot(spec.wavelength, spec.flux, label=r'$\log N_{\rm HI} = $'+'{:d}'.format(int(logN))) # Axes wvoff = 100. ax2.set_xlim(1215.-wvoff, 1215.+wvoff) ax2.set_ylim(-0.05, 1.1) #ax2.set_yscale("log", nonposy='clip') ax2.set_ylabel('Normalized Flux') ax2.set_xlabel(r'Wavelength ($\AA$)') ax2.xaxis.set_major_locator(plt.MultipleLocator(50.)) # set_spines(ax2, 2.) set_fontsize(ax2,csz) legend = ax2.legend(loc='lower left', scatterpoints=1, borderpad=0.3, handletextpad=0.3, fontsize='small', numpoints=1) # Write plt.tight_layout(pad=0.2,h_pad=0.,w_pad=0.1) plt.savefig(outfile, dpi=750) plt.close() print("Wrote {:s}".format(outfile))
def get_ions(self, use_Nfile=False, idict=None, update_zvlim=True, linelist=None, verbose=True): """Parse the ions for each Subsystem And put them together for the full system Fills ._ionN with a QTable Parameters ---------- idict : dict, optional dict containing the IonClms info use_Nfile : bool, optional Parse ions from a .clm file (JXP historical) NOTE: This ignores velocity constraints on components (i.e. chk_vel=False) update_zvlim : bool, optional Update zvlim from lines in .clm (as applicable) linelist : LineList """ if idict is not None: table = dict_to_ions(idict) self._ionN = table elif use_Nfile: # Subsystems if self.nsub > 0: # This speeds things up (but is rarely used) linelist = LineList('ISM') for lbl in self.subsys.keys(): clm_fil = self.tree+self.subsys[lbl]._datdict['clm_file'] # Parse .clm file self.subsys[lbl]._clmdict = igmau.read_clmfile(clm_fil, linelist=linelist) # Build components from lines components = ltiu.build_components_from_dict(self.subsys[lbl]._clmdict, coord=self.coord, chk_vel=False) self.subsys[lbl]._components = components # Update z, vlim if update_zvlim: self.update_vlim(sub_system=lbl) self.subsys[lbl].zabs = self.subsys[lbl]._clmdict['zsys'] # Read .ion file and fill in components ion_fil = self.tree+self.subsys[lbl]._clmdict['ion_fil'] self.subsys[lbl]._indiv_ionclms = igmau.read_ion_file(ion_fil, components) # Parse .all file all_file = ion_fil.split('.ion')[0]+'.all' self.subsys[lbl].all_file=all_file #MF: useful to have _ = igmau.read_all_file(all_file, components=components) # Build table self.subsys[lbl]._ionN = ltiu.iontable_from_components(components,ztbl=self.subsys[lbl].zabs) # Add to IGMSystem for comp in components: self.add_component(comp) # Combine if self.nsub == 1: self._ionN = self.subsys['A']._ionN self._clmdict = self.subsys['A']._clmdict #xdb.set_trace() elif self.nsub == 0: raise ValueError('lls_utils.get_ions: Cannot have 0 subsystems..') else: self._ionN = self.subsys['A']._ionN self._clmdict = self.subsys['A']._clmdict warnings.warn('lls_utils.get_ions: Need to update multiple subsystems!! Taking A.') else: raise ValueError("Need an option in get_ions")