def test_all_transitions(): error_msg = 'Something is wrong in all_transitions()' ism = LineList('ISM') #check simple case line = 'OVI' ovi_transitions = ism.all_transitions(line) assert len(ovi_transitions) == 2, error_msg #print(ovi_transitions['name']) #check unknown line = 'unknown' out = ism.all_transitions(line) assert type(out) == dict, error_msg #check case of single transition ion line = 'CIII' out = ism.all_transitions(line) assert type(out) == dict, error_msg #check case of transitions from excited levels line='FeII*' out = ism.all_transitions(line) assert len(out) == 27, "wrong line counts" print(out) # wrest out = ism.all_transitions(1215.6700*u.AA) assert len(out) == 30,"wrong line counts" # 30 Lyman series transitions #print('test_all_transitions() passed') h2 = LineList('H2') line = 'B19-0P(1)' out = h2.all_transitions(line) assert len(out) == 7
def test_unknown(): ism = LineList('ISM') unknown = ism.unknown_line() assert unknown[ 'name'] == 'unknown', 'There is a problem in the LineList.unknown_line()' assert unknown[ 'wrest'] == 0. * u.AA, 'There is a problem in the LineList.unknown_line()'
def from_dict(cls, idict, **kwargs): """ Instantiate from a dict Parameters ---------- idict : dict Required keys are: 'RA' -- float (deg) 'DEC' -- float(deg) 'zem' -- float 'name' -- str 'components' -- list Other keys are added as attributes to the IgmSightline object Returns ------- """ if 'linelist' not in kwargs.keys(): from linetools.lists.linelist import LineList ism = LineList('ISM') kwargs['linelist'] = ism from pyigm.abssys.utils import class_by_type ism = LineList('ISM') kwargs['linelist'] = ism # Load ISM to speed things up if 'meta' in idict.keys(): meta = idict['meta'] else: meta = idict # Components -- backwards compatability if 'cmps' in idict.keys(): idict['components'] = idict['cmps'].copy() # Instantiate slf = cls(SkyCoord(ra=meta['RA'], dec=meta['DEC'], unit='deg'), zem=meta['zem'], name=meta['JNAME'], **kwargs) # Other for key in idict.keys(): if key in [ 'RA', 'DEC', 'zem', 'name', 'components', 'meta', 'cmps' ]: continue else: setattr(slf, key, idict[key]) add_comps_from_dict(slf, idict, **kwargs) # Systems if 'systems' in idict.keys(): for key in idict['systems'].keys(): asys = class_by_type( idict['systems'][key]['abs_type']).from_dict( idict['systems'][key], **kwargs) slf._abssystems.append(asys) # Return return slf
def test_subset(): ism = LineList('ISM') subset = np.array([1215.6700, 1608.4511])*u.AA ism = ism.subset_lines(subset) assert len(ism._data) == 2 np.testing.assert_allclose(ism['FeII 1608']['wrest'], 1608.4511*u.AA, rtol=1e-7) # Now with names ism = LineList('ISM') subset = ['HI 1215', 'HI 1025', 'CIV 1548'] ism = ism.subset_lines(subset) np.testing.assert_allclose(ism['HI 1215']['wrest'], 1215.6700*u.AA, rtol=1e-7)
def set_llist(llist, in_dict=None, sort=True): """ Method to set a line list dict for the Widgets Parameters ---------- sort : bool, optional Sort lines by rest wavelength """ from linetools.lists.linelist import LineList from astropy.units.quantity import Quantity if in_dict is None: in_dict = dict(Lists=[]) if isinstance(llist,basestring): # Set line list from a file in_dict['List'] = llist in_dict['Lists'].append(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') llist_cls = llist_cls.subset_lines(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['Lists'].append('input.lst') in_dict['Plot'] = True # Fill if sort: llist.sort() llist_cls = LineList('ISM') llist_cls = llist_cls.subset_lines(llist) in_dict['input.lst'] = llist_cls else: raise IOError('Not ready for this type of input') # Return return in_dict
def fill_data(self, trans, linelist=None, closest=False, verbose=True): """ 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 : LineList, optional Class of linelist or str setting LineList closest : bool, optional Take the closest line to input wavelength? [False] """ # Deal with LineList if linelist is None: llist = LineList('ISM') elif isinstance(linelist,basestring): llist = LineList(linelist) elif isinstance(linelist,LineList): llist = linelist else: raise ValueError('Bad input for linelist') # Closest? llist.closest = closest # Data newline = llist[trans] try: self.data.update(newline) # Expected to be a LineList dict object except TypeError: pdb.set_trace() # Update self.wrest = self.data['wrest'] self.name = self.data['name'] # self.analy.update( { 'flg_eye': 0, 'flg_limit': 0, # No limit 'datafile': '', 'name': self.data['name'] }) # Additional fundamental attributes for Absorption Line self.attrib.update(abs_attrib.copy())
def init_lines(self): '''Fill up the component lines ''' if self.linelist is None: self.linelist = LineList('Strong') # Get the lines #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() all_trans = self.linelist.all_transitions(self.init_wrest) for trans in all_trans: self.lines.append(AbsLine(trans['wrest'], linelist=self.linelist)) # Sync self.sync_lines()
def measure_sodium_EW(filename): from linetools.lists.linelist import LineList from astropy import units as u from linetools.spectralline import AbsLine from linetools.spectra.xspectrum1d import XSpectrum1D import matplotlib.pyplot as plt sp = XSpectrum1D.from_file('RF_' + filename) sp.normalize(co=sp.co) wvlim = [5880, 6030] * u.AA strong = LineList('Strong') transitions = strong.available_transitions(wvlim, n_max_tuple=None, min_strength=0.0) line1 = transitions['wrest'][0] line2 = transitions['wrest'][1] avg_line = (line1 + line2) / 2.0 # Plot the spectrum to get limits for EW fig = plt.figure() plt.axvline(x=line1, color='k', linestyle='--') plt.axhline(y=1.0, color='r', linestyle='--') plt.axvline(x=line2, color='k', linestyle='--') sp.plot(xlim=(avg_line - 30, avg_line + 30)) S1 = AbsLine(transitions['wrest'][0] * u.AA, z=0.0) S1.analy['spec'] = sp S2 = AbsLine(transitions['wrest'][1] * u.AA, z=0.0) S2.analy['spec'] = sp #x = float(input("Enter a lower lim: ")) #y = float(input("Enter a higher lim: ")) x = 5888 y = 5896 S1.limits.set([x, y] * u.AA) S1.measure_ew(flg=1) # Measure the EW of the first line EW1 = S1.attrib['EW'], S1.attrib['sig_EW'] #x = float(input("Enter a lower lim: ")) #y = float(input("Enter a higher lim: ")) x = 5895 y = 5905 S2.limits.set([x, y] * u.AA) S2.measure_ew() # Measure the EW of the second line EW2 = S2.attrib['EW'], S2.attrib['sig_EW'] return EW1, EW2
def fill_data(self, trans, linelist=None, closest=False, verbose=True): """ 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 : LineList, optional Class of linelist or str setting LineList closest : bool, optional Take the closest line to input wavelength? [False] """ # Deal with LineList if linelist is None: 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 else: raise ValueError('Bad input for linelist') # Closest? llist.closest = closest # Data newline = llist[trans] if newline is None: raise ValueError("Transition {} not found in LineList {:s}".format(trans, llist.list)) try: self.data.update(newline) # Expected to be a LineList dict object except TypeError: raise TypeError("Probably should not be here") # Update self.wrest = self.data['wrest'] self.name = self.data['name'] # self.update() # is this used ?
def fill_data(self, trans, linelist=None, closest=False): """ 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 : LineList, optional Class of linelist or str setting LineList closest : bool, optional Take the closest line to input wavelength? [False] """ # Deal with LineList if linelist is None: llist = LineList('ISM') elif isinstance(linelist,basestring): llist = LineList(linelist) elif isinstance(linelist,LineList): llist = linelist else: raise ValueError('Bad input for linelist') # Closest? llist.closest = closest # Data newline = llist[trans] self.data.update(newline) # Update self.wrest = self.data['wrest'] self.trans = self.data['name'] # self.analy.update( { 'flg_eye': 0, 'flg_limit': 0, # No limit 'datafile': '', 'name': self.data['name'] }) # Additional attributes for Absorption Line self.attrib.update({'N': 0., 'Nsig': 0., 'flagN': 0, # Column 'b': 0.*u.km/u.s, 'bsig': 0.*u.km/u.s # Doppler } )
def read_ion_file(self,ion_fil,zabs=0.,RA=0.*u.deg, Dec=0.*u.deg): """Read in JXP-style .ion file in an appropriate manner NOTE: If program breaks in this function, check the all file to see if it is properly formatted. """ # Read names=('wrest', 'clm', 'sig_clm', 'flg_clm', 'flg_inst') table = ascii.read(ion_fil, format='no_header', names=names) if self.linelist is None: self.linelist = LineList('ISM') # Generate AbsLine's for row in table: # Generate the line aline = AbsLine(row['wrest']*u.AA, linelist=self.linelist, closest=True) # Set z, RA, DEC, etc. aline.attrib['z'] = self.zabs aline.attrib['RA'] = self.coord.ra aline.attrib['Dec'] = self.coord.dec # Check against existing lines mt = [kk for kk,oline in enumerate(self.lines) if oline.ismatch(aline)] if len(mt) > 0: mt.reverse() for imt in mt: print('read_ion_file: Removing line {:g}'.format(self.lines[imt].wrest)) self.lines.pop(imt) # Append self.lines.append(aline)
def test_euv(): euv = LineList('EUV') # assert np.max(euv._data['wrest']) < 1000. # Test for X-ray lines ovii = euv['OVII 21'] assert np.isclose(ovii['wrest'].value, 21.6019)
def _write_ref_ISM_table(): """ Write a reference table enabling faster I/O for ISM-related lists For developer use only. Note that after running this, you need to manually copy the table produced to linetools/data/lines/ISM_table.fits inside the github repository, and then check it in. """ from linetools.lists.linelist import LineList ism = LineList('ISM', use_ISM_table=False) strong = LineList('Strong', use_ISM_table=False) euv = LineList('EUV', use_ISM_table=False) hi = LineList('HI', use_ISM_table=False) # need a Table to write tab = ism._data.copy() tab.sort(('wrest')) # Using np.in1d doesn't work for some reason. Do it the long way cond = [] for table in (strong, euv, hi): igood = [] for row in Table(table._data): ind = tab['wrest'].searchsorted(row['wrest']) dw = abs(tab['wrest'][ind] - row['wrest']) if abs(tab['wrest'][ind + 1] - row['wrest']) < dw: ind = ind + 1 rism = tab[ind] # check this is the right row. if all(row[k] == rism[k] for k in hi._data.colnames if not hasattr(row[k], 'mask') or not row[k].mask): igood.append(ind) else: raise RuntimeError('No match found!') cond.append(np.zeros(len(tab), dtype=bool)) cond[-1][np.array(igood)] = True col1 = Column(cond[0], name='is_Strong') col2 = Column(cond[1], name='is_EUV') col3 = Column(cond[2], name='is_HI') tab.add_columns([col1, col2, col3]) tab.write('ISM_table.fits', overwrite=True)
def test_molecules(): h2 = LineList('H2') # for kk, name in enumerate(h2.name): lyi = AbsLine(name, linelist=h2) # gamma values h2_B3_0R0 = AbsLine('B3-0R(0)', linelist=h2) assert h2_B3_0R0.data['gamma'].value > 0.
def test_sortdata(): error_msg = 'Something is wrong in sortdata()' ism = LineList('ISM', sort_by='name') assert ism.name[0] == 'AlII 1670', error_msg ism.sortdata('name', reverse=True) assert ism.name[0] == 'ZrIII 1798', error_msg ism.sortdata(['abundance', 'rel_strength'], reverse=True) assert ism.name[0] == 'HI 1215', error_msg ism.sortdata(['rel_strength']) assert ism.name[0] == 'CI** 1123b', error_msg
def test_available_transitions(): error_msg = 'Something is wrong in available_transitions()' ism = LineList('ISM') wvlims = (900,1800)*u.AA z = 0.1 transitions = ism.available_transitions(wvlims/(1+z),n_max_tuple=5) assert transitions[2]['name'] == 'HI 972' , error_msg assert isinstance(transitions,Table), error_msg transitions = ism.available_transitions(wvlims/(1+z),n_max_tuple=2) assert transitions[2]['name'] == 'CIII 977' , error_msg wvlims = (1200,1800)*u.AA z = 0.5 transitions = ism.available_transitions(wvlims/(1+z), n_max_tuple=2) assert transitions[0]['name'] == 'HI 1025', error_msg assert 'OVI 1031' in transitions['name'], error_msg assert 'CIII 977' in transitions['name'], error_msg wvlims = (1000,3000)*u.AA z = 1.5 transitions = ism.available_transitions(wvlims/(1+z),n_max_tuple=2) assert 'NeVIII 770' in transitions['name'], error_msg assert 'MgX 609' in transitions['name'], error_msg assert 'HI 1215' not in transitions['name'], error_msg wvlims = (1215.6,1217)*u.AA z = 0 transitions = ism.available_transitions(wvlims/(1+z),n_max_tuple=2) assert isinstance(transitions,dict), error_msg
def test_mk_absline(): # Init HI Lya abslin = AbsLine(1215.6700 * u.AA) np.testing.assert_allclose(abslin.data['f'], 0.4164) # Init CII 1334 with LineList abslin2 = AbsLine(1334.5323 * u.AA, linelist='Strong', use_CACHE=True) np.testing.assert_allclose(abslin2.data['Ek'], 74932.62 / u.cm) # Init CII 1334 with multiple linelists ism = LineList('ISM') H2 = LineList('H2') abslin2b = AbsLine(1334.5323 * u.AA, linelist=[H2, ism]) np.testing.assert_allclose(abslin2.data['Ek'], 74932.62 / u.cm) # Init CII 1334 by name abslin3 = AbsLine('CII 1334') np.testing.assert_allclose(abslin3.data['wrest'], 1334.5323 * u.AA)
def load_sys(self, tfile=None, empty=True, debug=False, **kwargs): """ Load the COS-Halos survey from JSON files Empties the list Parameters ---------- tfile : str, optional empty : bool, optional Empty the list debug : bool, optional Only load the first 5 Returns ------- """ import tarfile import json from linetools.lists.linelist import LineList llist = LineList('ISM') # Tar file if tfile is None: tarfiles = glob.glob(self.cdir + 'cos-halos_systems.v*.tar.gz') tarfiles.sort() tfile = tarfiles[-1] print("Be patient, using {:s} to load".format(tfile)) # Empty if empty: self.cgm_abs = [] # Load tar = tarfile.open(tfile) for kk, member in enumerate(tar.getmembers()): if '.' not in member.name: print('Skipping a likely folder: {:s}'.format(member.name)) continue # Debug if debug and (kk == 5): break # Extract f = tar.extractfile(member) tdict = json.load(f) # Generate cgmsys = CGMAbsSys.from_dict(tdict, chk_vel=False, chk_sep=False, chk_data=False, use_coord=True, use_angrho=True, linelist=llist, **kwargs) self.cgm_abs.append(cgmsys) tar.close() # Werk+14 if ('Halos' in self.fits_path) and (self.werk14_cldy is not None): self.load_werk14()
def test_agn(): agn = LineList('AGN') # np.testing.assert_allclose(agn["Halpha"]['wrest'], 6564.613 * u.AA, rtol=1e-5) np.testing.assert_allclose(agn["NV 1242"]['wrest'], 1242.804 * u.AA, rtol=1e-5)
class Component(AbsComponent): def __init__(self, z, wrest, vlim=[-300.,300]*u.km/u.s, linelist=None): # Init self.init_wrest = wrest self.linelist = linelist self.lines = [] self.init_lines() # Generate with type radec = (0*u.deg,0*u.deg) Zion = (self.lines[0].data['Z'],self.lines[0].data['ion']) Ej = self.lines[0].data['Ej'] AbsComponent.__init__(self,radec, Zion, z, vlim, Ej, comment='None') # Init cont. self.attrib = {'N': 0./u.cm**2, 'Nsig': 0./u.cm**2, 'flagN': 0, # Column 'logN': 0., 'sig_logN': 0., 'b': 0.*u.km/u.s, 'bsig': 0.*u.km/u.s, # Doppler 'z': self.zcomp, 'zsig': 0., 'Quality': 'None'} # Sync self.sync_lines() # Use different naming convention here self.name = 'z{:.5f}_{:s}'.format( self.zcomp,self.lines[0].data['name'].split(' ')[0]) def init_lines(self): '''Fill up the component lines ''' if self.linelist is None: self.linelist = LineList('Strong') # Get the lines all_trans = self.linelist.all_transitions(self.init_wrest) #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() if isinstance(all_trans,dict): all_trans = [all_trans] for trans in all_trans: self.lines.append(AbsLine(trans['wrest'], linelist=self.linelist)) def sync_lines(self): '''Synchronize attributes of the lines ''' for line in self.lines: line.attrib['logN'] = self.attrib['logN'] line.attrib['b'] = self.attrib['b'] line.attrib['z'] = self.attrib['z']
def test_Wr_from_N_and_N_from_Wr(): hi_list = LineList('HI') lya = hi_list['HI 1215'] N = 10**np.linspace(12.0, 12.3, 4) / (u.cm * u.cm) # test array like Wr = Wr_from_N(N, lya['wrest'], lya['f']) Wr_test = [0.00544783, 0.00685842, 0.00863423, 0.01086986] * u.AA np.testing.assert_allclose(Wr, Wr_test, rtol=1e-5) N_new = N_from_Wr(Wr, lya['wrest'], lya['f']) np.testing.assert_allclose(N_new, N, rtol=1e-5)
def test_all_transitions(): error_msg = 'Something is wrong in all_transitions()' ism = LineList('ISM') #check simple case line = 'OVI' ovi_transitions = ism.all_transitions(line) assert len(ovi_transitions) == 2, error_msg #print(ovi_transitions['name']) #check unknown line = 'unknown' out = ism.all_transitions(line) assert type(out) == dict, error_msg #check case of single transition ion line = 'CIII' out = ism.all_transitions(line) assert type(out) == dict, error_msg # wrest out = ism.all_transitions(1215.6700*u.AA) assert len(out) == 30 # 30 Lyman series transitions
def test_strongest_transitions(): error_msg = 'Something is wrong in strongest_transitions()' ism = LineList('ISM') wvlims = (1200,1800)*u.AA z = 0.5 transitions = ism.strongest_transitions('HI',wvlims/(1+z),n_max=5) assert len(transitions) == 5, error_msg assert transitions[0]['name'] == 'HI 1025' , error_msg assert isinstance(transitions,QTable), error_msg wvlims = (1500,1700)*u.AA z = 0.5 transitions = ism.strongest_transitions('HI',wvlims/(1+z),n_max=5) assert isinstance(transitions,dict), error_msg #only Lyb should be available, so dict is expected assert transitions['name'] == 'HI 1025' wvlims = (1100,1200)*u.AA z = 0.0 transitions = ism.strongest_transitions('HI',wvlims/(1+z),n_max=5) assert transitions is None, error_msg
def test_strongest_transitions(): error_msg = 'Something is wrong in strongest_transitions()' ism = LineList('ISM') wvlims = (1200,1800)*u.AA z = 0.5 transitions = ism.strongest_transitions('HI',wvlims/(1+z),n_max=5) assert len(transitions) == 5, error_msg assert transitions[0]['name'] == 'HI 1025' , error_msg assert isinstance(transitions,Table), error_msg wvlims = (1500,1700)*u.AA z = 0.5 transitions = ism.strongest_transitions('HI',wvlims/(1+z),n_max=5) assert isinstance(transitions,dict), error_msg #only Lyb should be available, so dict is expected assert transitions['name'] == 'HI 1025' wvlims = (1100,1200)*u.AA z = 0.0 transitions = ism.strongest_transitions('HI',wvlims/(1+z),n_max=5) assert transitions is None, error_msg
def main(args=None): pargs = parser(options=args) if pargs.inp is None and pargs.all is False: print("No option selected. Use -h for Help") return # Setup from astropy import units as u from astropy.table import Column from linetools.lists.linelist import LineList from linetools.abund import ions as ltai import numpy as np if pargs.llist in ['CO']: cols = ['mol', 'label', 'wrest', 'f'] elif pargs.llist in ['H2']: cols = ['mol', 'name', 'wrest', 'f'] else: cols = ['name', 'wrest', 'f', 'A'] # LineList llist = LineList(pargs.llist) # Redshift? if float(pargs.redshift) != 0.: z = llist._data['wrest']*(1+float(pargs.redshift)) llist._data.add_column(Column(z, name='z')) cols += ['z'] # All? if pargs.all: try: llist._data[cols].pprint(99999) except ValueError: pdb.set_trace() return # Grab line(s) if ustr(pargs.inp[0]).isdecimal(): # Input rest wavelength wrest = float(pargs.inp)*u.AA mtch = np.abs(wrest-llist.wrest) < pargs.toler*u.AA llist._data[cols][mtch].pprint(99999) else: # Either ion or transition istrans = False for jj,char in enumerate(pargs.inp): if char.isdigit(): istrans = True i0 = jj break if istrans: trans = pargs.inp[0:i0]+' '+pargs.inp[i0:] tdict = llist[trans] for key,value in tdict.items(): if key in cols: print('{:s}: {}'.format(key,value)) else: # Ion Zion = ltai.name_ion(pargs.inp) mtion = (llist.Z == Zion[0]) & (llist.ion == Zion[1]) llist._data[cols][mtion].pprint(99999)
def test_available_transitions(): error_msg = 'Something is wrong in available_transitions()' ism = LineList('ISM') wvlims = (900,1800)*u.AA z = 0.1 transitions = ism.available_transitions(wvlims/(1+z),n_max_tuple=5) assert transitions[2]['name'] == 'HI 972' , error_msg assert isinstance(transitions,QTable), error_msg transitions = ism.available_transitions(wvlims/(1+z),n_max_tuple=2) assert transitions[2]['name'] == 'CIII 977' , error_msg wvlims = (1200,1800)*u.AA z = 0.5 transitions = ism.available_transitions(wvlims/(1+z), n_max_tuple=2) assert transitions[0]['name'] == 'HI 1025', error_msg assert 'OVI 1031' in transitions['name'], error_msg assert 'CIII 977' in transitions['name'], error_msg wvlims = (1000,3000)*u.AA z = 1.5 transitions = ism.available_transitions(wvlims/(1+z),n_max_tuple=2) assert 'NeVIII 770' in transitions['name'], error_msg assert 'MgX 609' in transitions['name'], error_msg assert 'HI 1215' not in transitions['name'], error_msg wvlims = (1215.6,1217)*u.AA z = 0 transitions = ism.available_transitions(wvlims/(1+z),n_max_tuple=2) assert isinstance(transitions,dict), error_msg
def set_llist(llist, in_dict=None, sort_by='wrest'): """ Method to set a line list dict for the Widgets Parameters ---------- sort_by : str or list of str, optional Key(s)to sort the lines by. Default is 'wrest'. If sort_by='as_given', it preserves the order as given by llist. """ from linetools.lists.linelist import LineList from astropy.units.quantity import Quantity if in_dict is None: in_dict = dict(Lists=[]) if isinstance(llist,basestring): # Set line list from a file in_dict['List'] = llist in_dict['Lists'].append(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', sort_by=sort_by) llist_cls = llist_cls.subset_lines(gdlines, sort_by='as_given') in_dict[llist] = llist_cls else: llist_cls = LineList(llist, sort_by=sort_by) # Load in_dict[llist] = llist_cls elif isinstance(llist, (Quantity, list)): # Set from a list of wrest in_dict['List'] = 'input.lst' in_dict['Lists'].append('input.lst') in_dict['Plot'] = True # Fill llist_cls = LineList('ISM', sort_by=sort_by) llist_cls = llist_cls.subset_lines(llist, sort_by=sort_by) in_dict['input.lst'] = llist_cls else: raise IOError('Not ready for this type of input') # Return return in_dict
def get_ions(self, idict=None, closest=False): """Parse the ions for each Subsystem And put them together for the full system Fills .ions with a Ions_Clm Class Parameters: ----------- closest : bool, optional Take the closest line to input wavelength? [False] idict : dict, optional dict containing the IonClms info """ if idict is not None: # Not setup for SubSystems self._ionclms = IonClms(idict=idict) else: # Subsystems if self.nsub > 0: # This speeds things up (but is rarely used) if self.linelist is None: self.linelist = LineList('ISM') lbls = map(chr, range(65, 91)) for ii in range(self.nsub): clm_fil = self.tree + self.subsys[lbls[ii]].clm_file # Parse .clm and .all files self.subsys[lbls[ii]].clm_analy = Ionic_Clm_File(clm_fil) ion_fil = self.tree + self.subsys[lbls[ii]].clm_analy.ion_fil all_fil = ion_fil.split('.ion')[0] + '.all' self.all_fil = all_fil #MF: useful to have self.subsys[lbls[ii]]._ionclms = IonClms(all_fil) # Linelist (for speed) if self.subsys[lbls[ii]].linelist is None: self.subsys[lbls[ii]].linelist = self.linelist self.subsys[lbls[ii]].linelist.closest = closest # Parse .ion file self.subsys[lbls[ii]].read_ion_file(ion_fil) # Combine if self.nsub == 1: self._ionclms = self.subsys['A']._ionclms self.clm_analy = self.subsys['A'].clm_analy self.lines = self.subsys['A'].lines #xdb.set_trace() elif self.nsub == 0: raise ValueError( 'lls_utils.get_ions: Cannot have 0 subsystems..') else: self._ionclms = self.subsys['A']._ionclms self.clm_analy = self.subsys['A'].clm_analy self.lines = self.subsys['A'].lines print( 'lls_utils.get_ions: Need to update multiple subsystems!! Taking A.' )
def plotspec(args): """Plot spectrum files """ from linetools.spectra.io import readspec import warnings import matplotlib.pyplot as plt import matplotlib as mpl warnings.simplefilter('ignore', mpl.mplDeprecation) plt.rcParams['axes.formatter.useoffset'] = False # avoid scientific notation in axes tick labels spec_cache = {} fig = plt.figure(figsize=(10,5)) fig.subplots_adjust(left=0.07, right=0.95, bottom=0.11) ax = fig.add_subplot(111) i = 0 quit = False print("#### Use left and right arrow keys to navigate, 'Q' to quit ####") while quit is False: filename = args.filenames[i] if filename not in spec_cache: spec_cache[filename] = readspec(filename) sp = spec_cache[filename] ax.cla() sp.plot(show=False) ax.set_xlabel(str(sp.wavelength.unit)) ax.set_title(filename) if args.redshift is not None: from linetools.lists.linelist import LineList ll = LineList('Strong') #import pdb ;pdb.set_trace() wlines = ll._data['wrest'] * (1 + args.redshift) y0, y1 = ax.get_ylim() ax.vlines(wlines.to(sp.wavelength.unit).value, y0, y1, linestyle='dotted') while True: plt.waitforbuttonpress() if sp._plotter.last_keypress == 'right': i += 1 i = min(i, len(args.filenames) - 1) # Note this only breaks out of the inner while loop break elif sp._plotter.last_keypress == 'left': i -= 1 i = max(i, 0) break elif sp._plotter.last_keypress == 'Q': quit = True break
def test_set_extra_columns_to_datatable(): # bad calls #ism = LineList('ISM') #with pytest.raises(ValueError) as tmp: # This is failing Python 2.7 for reasons unbenknownst to me # ism.set_extra_columns_to_datatable(abundance_type='incorrect_one') #ism = LineList('ISM') #with pytest.raises(ValueError): # ism.set_extra_columns_to_datatable(ion_correction='incorrect_one', redo=True) # test expected strongest value ism = LineList('ISM') #np.testing.assert_allclose(ism['HI 1215']['rel_strength'], 14.704326420257642) # THIS IS NO LONGER SUPPORTED tab = ism._extra_table np.testing.assert_allclose(np.max(tab['rel_strength']), 14.704326420257642)
def test_Wr_from_N_b(): hi_list = LineList('HI') lya = hi_list['HI 1215'] N = 10**np.linspace(12.0, 22.0, 4) / (u.cm * u.cm) b = np.ones_like(N.value) * 10 * u.km / u.s # test array like Wr = Wr_from_N_b(N, b, lya['wrest'], lya['f'], lya['gamma']) Wr_test = [5.30565005e-03, 1.92538448e-01, 1.60381902e+00, 7.31826938e+01 ] * u.AA np.testing.assert_allclose(Wr, Wr_test, rtol=1e-5) # test float like input for logN Wr = Wr_from_N_b(N[0], b[0], lya['wrest'], lya['f'], lya['gamma']) np.testing.assert_allclose(Wr, Wr_test[0], rtol=1e-5)
def linetools_linelist(self): '''Handler for a linetools LineList class. ''' if not hasattr(self, '_linetools_linelist'): # Only doing the import here so rest of the class doesn't # break if the feature isn't enabled. from linetools.lists.linelist import LineList self._linetools_linelist = LineList('ISM') return self._linetools_linelist
def main(*args, **kwargs): """ Runs the XAbsSysGui on input files """ import argparse parser = argparse.ArgumentParser(description='Parse for XAbsSys') parser.add_argument("spec_file", type=str, help="Spectral file") parser.add_argument("abssys_file", type=str, help="AbsSys file (JSON)") parser.add_argument("-outfile", type=str, help="Output filename") parser.add_argument("-llist", type=str, help="Name of LineList") #parser.add_argument("-exten", type=int, help="FITS extension") parser.add_argument("--un_norm", help="Spectrum is NOT normalized", action="store_true") pargs = parser.parse_args() from PyQt5.QtWidgets import QApplication from linetools.guis.xabssysgui import XAbsSysGui # Normalized? norm = True if pargs.un_norm: norm = False # Extension #exten = (pargs.exten if hasattr(pargs, 'exten') else 0) # Read spec keywords rsp_kwargs = {} # Line list if pargs.llist is not None: from linetools.lists.linelist import LineList llist = LineList(pargs.llist) else: llist = None # Read AbsSystem from linetools.isgm.abssystem import GenericAbsSystem abs_sys = GenericAbsSystem.from_json(pargs.abssys_file) #, chk_vel=False) app = QApplication(sys.argv) gui = XAbsSysGui(pargs.spec_file, abs_sys, norm=norm, llist=llist, outfil=pargs.outfile) gui.show() app.exec_()
def cgmsurvey_from_sightlines_fields(fields, sightlines, rho_max=300 * u.kpc, name=None, dummysys=True, embuffer=None, **kwargs): """Instantiate CGMAbsSurvey object from lists fo IgmGalaxyFields and IGMSightlines Parameters ---------- fields: list of IgmGalaxyFields sightlines : list of IGMSightlines name : str, optional Name for the survey 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 embuffer : Quantity, optional Velocity buffer between background source (e.g., QSO) and CGMAbsSys Returns ------- cgmsurvey: CGMAbsSurvey """ if ((not isinstance(fields, list)) | (not isinstance(sightlines, list)) | (len(fields) != len(sightlines))): raise IOError( "Inputs fields and sightlines must lists of the same length") if dummysys is True: from linetools.spectralline import AbsLine ismlist = LineList('ISM') from pyigm.cgm.cgmsurvey import CGMAbsSurvey cgmsys = [] for i, ff in enumerate(fields): print(ff.name) thiscgmlist = cgmabssys_from_sightline_field(ff, sightlines[i], rho_max=rho_max, dummysys=dummysys, linelist=ismlist, embuffer=embuffer, **kwargs) cgmsys.extend(thiscgmlist) if name is not None: cgmsurvey = CGMAbsSurvey.from_cgmabssys(cgmsys, survey=name) else: cgmsurvey = CGMAbsSurvey.from_cgmabssys(cgmsys) return cgmsurvey
def fill_data(self, trans, linelist=None, closest=False, verbose=True): """ 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 : LineList, optional Class of linelist or str setting LineList closest : bool, optional Take the closest line to input wavelength? [False] """ # Deal with LineList if linelist is None: 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 else: raise ValueError('Bad input for linelist') # Closest? llist.closest = closest # Data newline = llist[trans] if newline is None: raise ValueError("Transition {} not found in LineList {:s}".format( trans, llist.list)) 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 generate_line_list_file(ions = []): out = open('pyigm_line_list.txt', 'w') out.write('#Ion\tWavelength [A]\tgamma\t\tf_value\t\talt. name\n') llist = LineList('ISM') for ion in ions: ion_data = llist.all_transitions(ion.replace(" ", "")) print(ion) wls = ion_data['wrest'] gammas = ion_data['gamma'] fvals = ion_data['f'] altnames = ion_data['name'] if wls.size == 1: sys.stdout.flush() wls = [wls.value] gammas = [gammas.value] fvals = [fvals] altnames = [altnames] for wl, gamma, fval, altname in zip(wls, gammas, fvals, altnames): out.write("%s\t%0.6f\t%e\t%e\t%s\n"\ %(ion, wl, gamma, fval, altname)) out.close()
def __init__(self, spec, z, wrest, parent=None, vmnx=[-300., 300.] * u.km / u.s, norm=True, linelist=None): ''' spec = Spectrum1D ''' super(AODMWidget, self).__init__(parent) # Initialize self.spec = spec self.norm = norm self.z = z self.vmnx = vmnx self.wrest = wrest # Expecting (requires) units self.lines = [] if linelist is None: self.linelist = LineList('ISM') for iwrest in self.wrest: self.lines.append(AbsLine(iwrest, linelist=self.linelist)) self.psdict = {} # Dict for spectra plotting self.psdict[ 'x_minmax'] = self.vmnx.value # Too painful to use units here self.psdict['y_minmax'] = [-0.1, 1.1] self.psdict['nav'] = ltgu.navigate(0, 0, init=True) # Create the mpl Figure and FigCanvas objects. # self.dpi = 150 self.fig = Figure((8.0, 4.0), dpi=self.dpi) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self) self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus) self.canvas.setFocus() self.canvas.mpl_connect('key_press_event', self.on_key) self.canvas.mpl_connect('button_press_event', self.on_click) vbox = QtGui.QVBoxLayout() vbox.addWidget(self.canvas) self.setLayout(vbox) # Draw on init self.on_draw()
class Component(object): def __init__(self, z, wrest, vlim=[-300., 300] * u.km / u.s, linelist=None): # Init self.init_wrest = wrest self.zcomp = z self.vlim = vlim self.attrib = { 'N': 0., 'Nsig': 0., 'flagN': 0, # Column 'b': 0. * u.km / u.s, 'bsig': 0. * u.km / u.s, # Doppler 'z': self.zcomp, 'zsig': 0. } # self.linelist = linelist self.lines = [] self.init_lines() # self.name = 'z{:.5f}_{:s}'.format( self.zcomp, self.lines[0].data['name'].split(' ')[0]) # def init_lines(self): '''Fill up the component lines ''' if self.linelist is None: self.linelist = LineList('Strong') # Get the lines #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() all_trans = self.linelist.all_transitions(self.init_wrest) for trans in all_trans: self.lines.append(AbsLine(trans['wrest'], linelist=self.linelist)) # Sync self.sync_lines() def sync_lines(self): '''Synchronize attributes of the lines ''' for line in self.lines: line.attrib['N'] = self.attrib['N'] line.attrib['b'] = self.attrib['b'] line.attrib['z'] = self.attrib['z']
def init_lines(self): '''Fill up the component lines ''' if self.linelist is None: self.linelist = LineList('Strong') # Get the lines all_trans = self.linelist.all_transitions(self.init_wrest) #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() if isinstance(all_trans,dict): all_trans = [all_trans] for trans in all_trans: self.lines.append(AbsLine(trans['wrest'], linelist=self.linelist))
def Wr_from_N_b_transition(N, b, transition, llist='ISM'): """ For a given transition this function looks for the atomic parameters (wa0, fosc, gamma) and returns the rest-frame equivalent width for a given N and b. It uses the approximation given by Draine 2011 book (eq. 9.27), which comes from atomic physics considerations See also Rodgers & Williams 1974 (NT: could not find the reference given by Draine) Parameters ---------- N : Quantity or Quantity array Column density b : Quantity or Quantity array of same shape as N Doppler parameter transition : str Name of the transition using linetools' naming convention, e.g. 'HI 1215'. llist : str Name of the linetools.lists.linelist.LineList object where to look for the transition name. Default is 'ISM', which means the function looks within `list = LineList('ISM')`. Returns ------- Wr : Quantity Rest-frame equivalent width Notes ----- See also Wr_from_N_b() """ linelist = LineList(llist) transition_dict = linelist[transition] if transition_dict is None: raise ValueError( 'Transition {:s} not found within LineList {:s}'.format( transition, linelist.list)) else: # get atomic parameters wrest = transition_dict['wrest'] fosc = transition_dict['f'] gamma = transition_dict['gamma'] # return return Wr_from_N_b(N, b, wrest, fosc, gamma)
class Component(object): def __init__(self, z, wrest, vlim=[-300.,300]*u.km/u.s, linelist=None): # Init self.init_wrest = wrest self.zcomp = z self.vlim = vlim self.attrib = {'N': 0., 'Nsig': 0., 'flagN': 0, # Column 'b': 0.*u.km/u.s, 'bsig': 0.*u.km/u.s, # Doppler 'z': self.zcomp, 'zsig': 0., 'Quality': 'None'} self.comment = 'None' # self.linelist = linelist self.lines = [] self.init_lines() # self.name = 'z{:.5f}_{:s}'.format( self.zcomp,self.lines[0].data['name'].split(' ')[0]) # def init_lines(self): '''Fill up the component lines ''' if self.linelist is None: self.linelist = LineList('Strong') # Get the lines all_trans = self.linelist.all_transitions(self.init_wrest) #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() if isinstance(all_trans,dict): all_trans = [all_trans] for trans in all_trans: self.lines.append(AbsLine(trans['wrest'], linelist=self.linelist)) # Sync self.sync_lines() def sync_lines(self): '''Synchronize attributes of the lines ''' for line in self.lines: line.attrib['N'] = self.attrib['N'] line.attrib['b'] = self.attrib['b'] line.attrib['z'] = self.attrib['z']
def test_set_extra_columns_to_datatable(): ism = LineList('ISM') # bad calls try: ism.set_extra_columns_to_datatable(abundance_type='incorrect_one') except ValueError: pass try: ism.set_extra_columns_to_datatable(ion_correction='incorrect_one') except ValueError: pass # test expected strongest value ism.set_extra_columns_to_datatable(ion_correction='none', abundance_type='solar') np.testing.assert_allclose(ism['HI 1215']['rel_strength'], 14.704326420257642) tab = ism._data np.testing.assert_allclose(np.max(tab['rel_strength']), 14.704326420257642)
def add_abslines_from_linelist(self, llist='ISM', wvlim=None, min_Wr=None, **kwargs): """ It adds associated AbsLines satisfying some conditions (see parameters below). Parameters ---------- llist : str 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')`. wvlims : 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) name = ions.ion_name(self.Zion, nspace=0) transitions = llist.all_transitions(name) # unify output to be always QTable if isinstance(transitions, dict): transitions = llist.from_dict_to_qtable(transitions) # check wvlims if wvlim is not None: cond = (transitions['wrest']*(1+self.zcomp) >= wvlim[0]) & \ (transitions['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) 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 test_unknown(): ism = LineList('ISM') unknown = ism.unknown_line() assert unknown['name'] == 'unknown', 'There is a problem in the LineList.unknown_line()' assert unknown['wrest'] == 0.*u.AA, 'There is a problem in the LineList.unknown_line()' print(ism['unknown'])
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. """ from linetools.lists import utils as ltlu # 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 = ltlu.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 if self.logN == 0: pass else: N = 10.**self.logN / u.cm**2 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 test_closest(): ism = LineList('ISM') ism.closest=True # line = ism[1250.584*u.AA] np.testing.assert_allclose(line['wrest'], 1250.578*u.AA, rtol=1e-7)
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 ?