def test_use(): # Init llim = zLimits(1., (0.999, 1.001), wrest=1215.67 * u.AA) # Use np.testing.assert_allclose(llim.zlim, (0.999, 1.001)) np.testing.assert_allclose(llim.wvlim.value, [2430.12433, 2432.55567]) np.testing.assert_allclose(llim.vlim.value, [-149.93370, 149.858755]) assert llim.vlim.unit == u.km / u.s # Print print(llim)
def test_set(): # Init zlim = (0.999, 1.001) llim = zLimits(1., zlim, wrest=1215.67 * u.AA) # Set llim.set(zlim) np.testing.assert_allclose(llim.wvlim.value, [2430.12433, 2432.55567]) llim.set([2430., 2433.] * u.AA) np.testing.assert_allclose(llim.vlim.value, [-165.27207033, 204.61374917]) llim.set([-160., 200] * u.km / u.s)
def test_init(): # Init zlim = (0.999, 1.001) llim = zLimits(1., zlim) # Init zlim = (0.999, 1.001) llim = zLimits(1., zlim, wrest=1215.67 * u.AA) # Test with pytest.raises(AttributeError): llim.zlim = 3 # AbsLine lya = AbsLine('HI 1215') z = 1. zlim = (0.999, 1.001) llim = zLimits.from_specline(lya, z, zlim) # Bad zlim with pytest.raises(IOError): llim = zLimits(1., (1.1, 1.2), wrest=1215.67 * u.AA, chk_z=True) # Null zlim llim = zLimits(1., (1., 1), wrest=1215.67 * u.AA) assert llim.is_set() is 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 __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 test_to_dict(): # Init llim = zLimits(1., (0.999, 1.001), wrest=1215.67 * u.AA) ldict = llim.to_dict() for key in ['vlim', 'wrest', 'wvlim', 'z', 'zlim']: assert key in ldict.keys()
def from_dict(cls, idict, coord=None, warn_only=False, chk_data=True, **kwargs): """ Initialize from a dict (usually read from disk) Parameters ---------- idict : dict dict with the Line parameters chk_data : bool, optional Check atomic data in dict against current values in LineList warn_only : bool, optional If the chk_data is performed and the values do not match, only throw a Warning as opposed to crashing Returns ------- sline : SpectralLine SpectralLine of the proper type """ # Init if idict['ltype'] == 'Abs': # TODO: remove this try/except eventually try: sline = AbsLine(idict['name'], **kwargs) except KeyError: # This is to be compatible JSON files already written with old notation (e.g. DLA H100) sline = AbsLine(idict['trans'], **kwargs) elif idict['ltype'] == 'Em': sline = EmLine(idict['name'], **kwargs) else: raise ValueError("Not prepared for type {:s}.".format(idict['ltype'])) # Check data if chk_data: #for key in idict['data']: for key in sline.data.keys(): if key not in idict['data'].keys(): warnings.warn("Key {:s} not in your input dict".format(key)) continue if isinstance(idict['data'][key], dict): # Assume Quantity val = idict['data'][key]['value'] else: val = idict['data'][key] try: assert sline.data[key] == val except AssertionError: if warn_only: warnings.warn("Different data value for {:s}: {}, {}".format(key,sline.data[key],val)) # Set analy for key in idict['analy'].keys(): if isinstance(idict['analy'][key], dict): # Assume Quantity #sline.analy[key] = Quantity(idict['analy'][key]['value'], # unit=idict['analy'][key]['unit']) #pdb.set_trace() sline.analy[key] = ltu.convert_quantity_in_dict(idict['analy'][key]) elif key == 'spec_file': # spec_file is intended to be the name of the spectrum file # spec is intended to hold an XSpectrum1D object #warnings.warn("You will need to load {:s} into analy['spec'] yourself".format( # idict['analy'][key])) sline.analy[key] = idict['analy'][key] else: sline.analy[key] = idict['analy'][key] # Set attrib for key in idict['attrib'].keys(): if isinstance(idict['attrib'][key], dict): sline.attrib[key] = ltu.convert_quantity_in_dict(idict['attrib'][key]) elif key in ['RA','DEC']: if coord is None: sline.attrib['coord'] = SkyCoord(ra=idict['attrib']['RA']*u.deg, dec=idict['attrib']['DEC']*u.deg) else: sline.attrib['coord'] = coord else: sline.attrib[key] = idict['attrib'][key] # Set z and limits if 'z' in sline.attrib.keys(): # Backwards compatability z = sline.attrib.pop('z') else: z = 0. if 'limits' in idict.keys(): if 'wrest' not in idict['limits'].keys(): # compatibility with IGMGuesses # import pdb; pdb.set_trace() idict['limits']['wrest'] = ltu.jsonify(sline.wrest) idict['limits']['z'] = z sline.limits = zLimits.from_dict(idict['limits']) else: sline.limits = zLimits(z, [z,z], wrest=sline.wrest) if 'vlim' in sline.analy.keys(): # Backwards compatability if sline.analy['vlim'][1] > sline.analy['vlim'][0]: sline.limits.set(sline.analy['vlim']) elif 'wvlim' in sline.analy.keys(): # Backwards compatability if sline.analy['wvlim'][1] > sline.analy['wvlim'][0]: sline.limits.set(sline.analy['wvlim']) return sline
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 from_dict(cls, idict, coord=None, warn_only=False, chk_data=True, **kwargs): """ Initialize from a dict (usually read from disk) Parameters ---------- idict : dict dict with the Line parameters chk_data : bool, optional Check atomic data in dict against current values in LineList warn_only : bool, optional If the chk_data is performed and the values do not match, only throw a Warning as opposed to crashing Returns ------- sline : SpectralLine SpectralLine of the proper type """ # Init if idict['ltype'] == 'Abs': # TODO: remove this try/except eventually try: sline = AbsLine(idict['name'], **kwargs) except KeyError: # This is to be compatible JSON files already written with old notation (e.g. DLA H100) sline = AbsLine(idict['trans'], **kwargs) elif idict['ltype'] == 'Em': sline = EmLine(idict['name'], **kwargs) else: raise ValueError("Not prepared for type {:s}.".format( idict['ltype'])) # Check data if chk_data: #for key in idict['data']: for key in sline.data.keys(): if key not in idict['data'].keys(): warnings.warn( "Key {:s} not in your input dict".format(key)) continue if isinstance(idict['data'][key], dict): # Assume Quantity val = idict['data'][key]['value'] else: val = idict['data'][key] try: assert sline.data[key] == val except AssertionError: if warn_only: warnings.warn( "Different data value for {:s}: {}, {}".format( key, sline.data[key], val)) # Set analy for key in idict['analy'].keys(): if isinstance(idict['analy'][key], dict): # Assume Quantity #sline.analy[key] = Quantity(idict['analy'][key]['value'], # unit=idict['analy'][key]['unit']) #pdb.set_trace() sline.analy[key] = ltu.convert_quantity_in_dict( idict['analy'][key]) elif key == 'spec_file': # spec_file is intended to be the name of the spectrum file # spec is intendended to hold an XSpectrum1D object warnings.warn( "You will need to load {:s} into analy['spec'] yourself". format(idict['analy'][key])) sline.analy[key] = idict['analy'][key] else: sline.analy[key] = idict['analy'][key] # Set attrib for key in idict['attrib'].keys(): if isinstance(idict['attrib'][key], dict): sline.attrib[key] = ltu.convert_quantity_in_dict( idict['attrib'][key]) elif key in ['RA', 'DEC']: if coord is None: sline.attrib['coord'] = SkyCoord( ra=idict['attrib']['RA'] * u.deg, dec=idict['attrib']['DEC'] * u.deg) else: sline.attrib['coord'] = coord else: sline.attrib[key] = idict['attrib'][key] # Set z and limits if 'z' in sline.attrib.keys(): # Backwards compatability z = sline.attrib.pop('z') else: z = 0. if 'limits' in idict.keys(): if 'wrest' not in idict['limits'].keys( ): # compatibility with IGMGuesses # import pdb; pdb.set_trace() idict['limits']['wrest'] = ltu.jsonify(sline.wrest) idict['limits']['z'] = z sline.limits = zLimits.from_dict(idict['limits']) else: sline.limits = zLimits(z, [z, z], wrest=sline.wrest) if 'vlim' in sline.analy.keys(): # Backwards compatability if sline.analy['vlim'][1] > sline.analy['vlim'][0]: sline.limits.set(sline.analy['vlim']) elif 'wvlim' in sline.analy.keys(): # Backwards compatability if sline.analy['wvlim'][1] > sline.analy['wvlim'][0]: sline.limits.set(sline.analy['wvlim']) return sline
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 = []
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 = []