forked from trystynb/LineFinder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtb_linefinder.py
1705 lines (1537 loc) · 69.5 KB
/
tb_linefinder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/python2.7
"""
TB_LINEFINDER.PY
v1.2.1 (5/02/2016)
Previous version was not loading limits properly from log. This is now fixed.
v1.2 (11/01/2016)
MAJOR revisions are:
-Changed element selection in LINEADDER from radiobuttons to checkbox
(fixes unselecting elements)
-Changed log-writing/editing to a GUI window rather than through terminal window (UPDATELOG class)
-Can now left/right clcik on spectrum display to set velocity limits!
-Fixed behaviour of log-editing drop down menus (they now appear to update when a line/system/ion is removed)
-NOTE: This doesn't fix the bug if there isn't a redshift/line/ion in the log file, but
it at least will notify the user that the selected redshift is "<Z>", etc
-Tutorial mode is now off by default!
MINOR revisions not noted, but minor code adjustments to plots, code, etc.
V1.1 (10/07/2015)
-Added a file browser button for selecting files.
-Added a tutorial mode that can be turned on/off in the main menu.
Author: Trystyn Berg ([email protected])
Stand-alone python application to identify spectral lines and save selected line list.
PYTHON PACKAGE REQUIREMENTS:
TB_LINEFINDER was developed with:
-Python Version 2.7
-Matplotlib Version 1.4.1
-Need matplotlib.backends.backend_tkagg
-Tkinter Revision: 81008
-Numpy
Other verisons of above packages may work...
INPUT FILE REQUIREMENTS
-ASCII file containing spectrum for plotting.
Each line in file must be in the format (whitespace delimeted):
WAVELENGTH FLUX
Any additional columns after first two are ignored.
MUST CONTAIN THE SAME NUMBER OF COLUMNS IN EACH LINE (unless commented)
Can use '#' at the start of any line to remove comment
-ASCII file containing a line list for identification.
Each line in the file must contain information on each spectral line
of interest in the format (whitespace delimited):
REST_WAVELENGTH ION SHORT_WL OSCILLATOR_STRENGTH
REST_WAVELENGTH - The rest wavelength of the spectral line (e.g. 1215.6701)
ION - The species (including ionization state) of the spectral line (e.g. HI)
SHORT_WL - A short identifier of the wavelength (e.g. 1215)
Needed to descrimante between absorption lines of a given ION
OSCILLATOR_STRENGTH - The oscillator strength of the feature (e.g. 0.4164)
This is only used for display purposes.
EXAMPLE:
1215.6701 HI 1215 0.4164
If Ly-alpha not present, TB_LINEFINDER will add it by default
(Morton 2003 values, as in example above)
MUST CONTAIN THE SAME NUMBER OF COLUMNS IN EACH LINE (unless commented)
Can use '#' at the start of any line to remove or comment
OPTIONAL INPUT
-TB_LINEFINDER log file (see OUTPUT FILE below for description)
OUTPUT FILE
-TB_LINEFINDER will generate a log file with the line list.
The output is a semicolon (;) delimited
Each line represents an spectral line identified in the format:
Z; ION; LINE; FLAG; VMIN; VMAX; NOTES; COLOUR;
Z - Redshift (to 5 decimal places) of line identified
ION - The Species of the line identified (from input linelist)
LINE - The SHORT_WL identifier from the input linelist
FLAG - A integer associated with the quality of the identified line
VMIN - The bluemost velocity (km/s) of the line profile
VMAX - The redmost velocity (km/s) of the line profile
NOTES - A string with any notes the user inputs
COLOUR - The matplotlib colour to denote feature in TB_LINEFINDER
The FLAG notation is a binary format with the following option
0 - No good/skip
1 - OK
2 - Blend
4 - Upper Limit/Non-detection
8 - Lower Limit/Saturated
When the output logfile is being read, any line beginning with '#'
will be ignored
For the purpose of future reference, the first two lines of the
logfile contain the input filenames in the format:
#!Line List: <INPUT LINELIST ASCII FILENAME>
#!Spectrum: <INPUT SPECTRUM ASCII FILENAME>
KNOWN ISSUES:
Often MATPLOTLIB will change some features, which affect how the user can
interact with the application. If problem, please check your matplotlib version
"""
##############################
###INTIALIZATION OF PROGRAM###
##############################
#Import necessary Matplotlib packages
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import matplotlib.pyplot as plt
#Import Tkinter for GUI interface
import Tkinter, tkFileDialog, tkMessageBox
#import other basic pacakages
import numpy as np
import math
import os,sys
#Default input/output files necessary for running
initinspec='test.ascii'
initllist='linelist.lst'
initlog='linefinder.log'
#Command line inputs (Can be changed or set within GUI)
inputs=sys.argv
if len(sys.argv)==4:
initinspec=sys.argv[1]
initllist=sys.argv[2]
initlog=sys.argv[3]
#List of colours to use (must be Matplotlib colours)
mplcolours=['r','k','b','c','m','g']
#DEBUG set to TRUE enables print statements throughout code for debugging purposes
debug=False
#debug=True
#USETUTORIAL is a boolean to control message boxes that help use the software.
#Thic can be turned on/off using the menu in the main Linefinder window.
#usetutorial=True
usetutorial=False
def velspec(line, wvlngth, vmin,vmax, spectrum):
"""
VELSPEC takes a spectrum and converts the wavelength scale
into a velocity scale (in km/s) based on an input wavelength
call - TMPVEL,TMPSPEC=velspec(LINE,WVLNGTH,VMIN,VMAX,SPECTRUM)
INPUT VARIABLES:
LINE - The wavelength to zero the velocity scale to (float)
WVLGNTH - A NUMPY array with the wavelength information of the spectrum
VMIN - The minimum velocity of the returned velocity scale (float)
VMAX - The maximum velocity of the returned velocity scale (float)
SPECTRUM - A NUMPY array with the flux information of the spectrum
RETURNS:
TMPVEL - A NUMPY array of the Velocity-convereted scale (between VMIN and VMAX)
TMPSPEC - A slice of the input SPECTRUM, with each element corresponding to the
flux at the same element in the outputted TMPVEL
NOTES:
If there is no coverage of the velocity specturm between VMIN and VMAX,
TMPSPEC will contain an array of zeros. If this occurs, a warning will
be printed to the screen.
"""
c=3.0E5# speed of light (km/s)
vel=np.array((wvlngth-line)/line*c)#Convert to a velocity
#Check to see that VEL is within the specified VMIN/VMAX range
if min(vel)<vmin and max(vel)>vmax:
ind1=(min(np.where(vel>vmin)[0]))#Find the index closest to vmin
ind2=(max(np.where(vel<vmax)[0]))#Find the index closest to vmax
tmpvel=vel[ind1:ind2]#Generate the velocity (x) array of intere$
tmpspec=spectrum[ind1:ind2]#Do the Same for the relative flux(y)
return tmpvel, tmpspec#Return slice of spectrum
#If not, return zero arrays and print warning
else:
print 'VELSPEC: Line not in velocity range'
return vel, np.zeros(len(vel))
def specfits(infile):
"""
SPECFITS reads the input spectrum ASCII file and returns the data in two NUMPY arrays
Call - WVLNGTH,SPECTRUM=specfits(INFILE)
INPUT VARIABLE:
INFILE - The input spectrum file.
Each line in file must be in the format (whitespace delimeted):
WAVELENGTH FLUX
Any additional columns after first two are ignored.
MUST CONTAIN THE SAME NUMBER OF COLUMNS IN EACH LINE (unless commented)
Can use '#' at the start of any line to remove comment
OUTPUT VARIABLES:
WVLNGTH - A NUMPY array with the wavelength (first column) of the input spectrum
SPECTRUM - A NUMPY array with the flux (second column) of the input spectrum
NOTES:
If no input spectrum file is found, WVLNGTH and SPECTRUM
will be NUMPY arrays full of zeros. A warning will be printed
to the screen.
"""
#Check to see if file exists
if os.path.isfile(infile):
#REad spectrum file. All values must be floats
#Will ignore lines with '#' flag
#First column must be wavelength
#Second column must be flux
data=np.genfromtxt(infile,dtype=type(0.00),comments='#')
wvlngth=data[:,0]
spectrum=data[:,1]
#Return spectrum data
return wvlngth,spectrum
#If file doesn't exist
else:
#Print warning
print "WARNING: No spectrum file found"
#Return zero arrays
return np.zeros(100),np.zeros(100)
def IsFloat(str):
"""
ISFLOAT checks if a given string is a float.
Call - OUTPUT=IsFloat(STR)
INPUT VARIABLE:
STR - A string for checking whether a float or not
OUTPUT VARIABLE:
OUTPUT - A boolean value corresponding to whether STR
is a float (returns TRUE) or not (returns FALSE)
"""
#See if STR is a float
try:
float(str)
#If test fails, return FALSE
except Exception:
return False
#If passed the test, return TRUE
return True
def WriteLog(log,logfile,llistfile,fits):
"""
WRITELOG writes the TB_LINEFINDER output logfile.
call WriteLog(LOG,LOGFILE,LLISTFILE,FITS)
INPUT VARIABLES:
LOG - A dictionary with the log information (see LOADLOG for structure)
LOGFILE - A string with the desired filename for the output logfile
LLISTFILE - A string with the input linelist filename
FITS - A string with the input spectrum filename
NOTES:
If no logfile is provided, it will not save log and display warning
"""
#Check to see if LOGFILE string has at least one character
if len(logfile)>1:
#Open LOGFILE buffer for writing
f=open(logfile,'w')
#Write the input files used to generate TB_LINEFINDER logfile
f.write('#!Line List: %s\n'%llistfile)
f.write('#!Spectrum: %s\n'%fits)
#Write column labels for user reference
f.write('#z;\t\tIon;\tline;\tflag;\tvmin;\tvmax;\tNotes;\tcolour\n')
#Loop through the LOG dictionary (redshift, ion/species, line identifier)
for z in log['zs']:
for ion in log['ions']:
for line in log['lines'][ion]:
#check if entry (keyed by z,ion,line) exists in log
if (z,ion,line) in log:
#If so, write to log file in format:
#Z; ION; LINE; FLAG; VMIN; VMAX; NOTES; COLOUR;
f.write('%s;\t'%z)
f.write('%s;\t'%ion)
f.write('%s;\t'%line)
f.write('%s;\t'%log[z,ion,line,'flag'])
f.write('%s;\t%s;\t'%log[z,ion,line,'vel'])
f.write('%s;\t'%log[z,ion,line])
f.write('%s;\n'%log[z,ion,line,'colour'])
#close Logfile writing buffer
f.close()
#Print stuff informing user of WRITELOG's success (including contents
#of file
print "Wrote logfile: %s"%logfile
print "Contents:\n"
print "***********\n"
os.system('cat %s'%logfile)
print "\n***********"
#If LOGFILE doesn't have a single character, cannot save. Print Warning
else: print "No log file provided. Did not Save."
def LoadLog(logfile):
"""
LOADLOG loads the log file.
Call: LoadLog(LOGFILE)
INPUTS:
LOGFILE - String containing filename out TB_LINEFINDER output logfile.
The format of the log file is as follows:
For the purpose fo future reference, the first two lines of the
logfile contain the input filenames in the format:
#!Line List: <INPUT LINELIST ASCII FILENAME>
#!Spectrum: <INPUT SPECTRUM ASCII FILENAME>
The file is a semicolon (;) delimited
Each line represents an spectral line identified in the format:
Z; ION; LINE; FLAG; VMIN; VMAX; NOTES; COLOUR;
Z - Redshift (to 5 decimal places) of line identified
ION - The Species of the line identified (from input linelist)
LINE - The SHORT_WL identifier from the input linelist
FLAG - A integer associated with the quality of the identified line
VMIN - The bluemost velocity (km/s) of the line profile
VMAX - The redmost velocity (km/s) of the line profile
NOTES - A string with any notes the user inputs
COLOUR - The matplotlib colour to denote feature in TB_LINEFINDER
The FLAG notation is a binary format with the following option
0 - No good/skip
1 - OK
2 - Blend
4 - Upper Limit/Non-detection
8 - Lower Limit/Saturated
When the output logfile is being read, any line beginning with '#'
will be ignored
OUTPUT:
LOG - A dictionary with all the log information.
The dictionary is keyed in the following way
LOG['zs'] - A list of redshifts for each system
LOG['ions'] - A list of all ion species (e.g. HI, CIV) within LOG.
All ion species should be found in the linelist input to TB_LINEFINDER
LOG['lines']- A dictionary of all the lines within LOG. This dictionary
is keyed by all the ion species in LOG['ions'].
For each ION in LOG['ions']:
LOG['lines'][ION] - A list of all the lines (identified by the
SHORT_WL identifier in the linelist input to TB_LINEFINDER
LOG[Z,ION,LINE] - COntains the NOTES information in the LOG dictionary. It
is keyed by (Z,ION,LINE), which are found in the lists of
LOG['zs'], LOG['ions'], and LOG['lines'][ION] (respectively)
LOG[Z,ION,LINE,'flag'] - COntains the FLAG information in the LOG dictionary. It
is keyed by (Z,ION,LINE,'flag'), which are found in the lists of
LOG['zs'], LOG['ions'], and LOG['lines'][ION] (respectively)
LOG[Z,ION,LINE,'colour'] - COntains the COLOUR information in the LOG dictionary. It
is keyed by (Z,ION,LINE,'colour'), which are found in the lists of
LOG['zs'], LOG['ions'], and LOG['lines'][ION] (respectively)
LOG[Z,ION,LINE,'vel'] - COntains the tuple (VMIN,VMAX) from the LOG dictionary. It
is keyed by (Z,ION,LINE,'vel'), which are found in the lists of
LOG['zs'], LOG['ions'], and LOG['lines'][ION] (respectively)
NOTES:
If no LOGFILE string is found in the current directory, a blank
LOG dictionary is loaded, with LOG['ion'], LOG['zs'], and LOG['lines']
generated. The file will be created at saving the log.
"""
#THe main LOG dictionary
log={}
#Key LOG withe the redshift and ion lists
log['zs']=[]
log['ions']=[]
#Key LOG with a sub-dictionary of the spectra lines
log['lines']={}
#Check if the LOGFILE all ready exists, and read it.
if os.path.isfile(logfile):
print "Loading logfile: %s"%logfile
#Read in the semicolon delimted file
data=np.genfromtxt(logfile,delimiter=';',dtype=type('str'),comments='#')
if len(data)>0:
#For each line in the file
for ii in range(len(data)):
#REad in each column, remove whitespace around the string,
#and format the data.
z=data[ii][0].strip()
ion=data[ii][1].strip()
line=data[ii][2].strip()
#If Z isn't in 'zs' list, add it.
if z not in log['zs']: log['zs'].append(z)
#If ION isn't in 'ions' list, add it.
#Also, need to add ION as a key to 'lines' dictionary
if ion not in log['ions']:
log['ions'].append(ion)
log['lines'][ion]=[]
#If LINE isn't in ['lines'][ION], include it
if line not in log['lines'][ion]: log['lines'][ion].append(line)
log[z,ion,line,'flag']=data[ii][3].strip()
log[z,ion,line,'vel']=data[ii][4].strip(), data[ii][5].strip()
log[z,ion,line]=data[ii][6].strip()#Add NOTE
log[z,ion,line,'colour']=data[ii][7].strip()
#If no LOGFILE is found, inform the user that is the case.
else:
print "Logfile not found at startup. Will create %s on exit"%logfile
#return LOG
return log
class UpdateLog:
"""
CLASS UPDATELOG - The GUI environment for editing and updating the LOG file/dictionary for a given
absorption feature. A plot of the absorption line will be displayed, along with a window to
set the log for the absortption feature
This will allow the user to set the velocity bounds of the absroption
feature (left/right clicking on the display window), the flags of the line, any notes/comments,
and the colour for the LINEFINDER display. A
Call - UL=UpdateLog(LOG,Z,ION,LINE,WVL,IWVLNGTH,ISPECTRUM)
INPUTS:
LOG - The log dictionary
Z - The redshift (float) of the system of interest
ION - The name of the ion in the log file that is to be edited
LINE - The wavelength identifier of the line to be edited
WVL - The real wavelength (float) of the absorption line (from LLIST?)
IWVLNGTH - The array of wavelengths of the input spectrum
ISPECTRUM - The array of fluxes of the input spectrum
ATTRIBUTES:
UL.LOG - The log dictionary
UL.ZSTR - The String of redshift
UL.ION - The ion of the absroption feature
UL.LINE - The wavelength identifier of the absorption feature
UL.ULMASTER - The TKinter window master
UL.ULFRAME - The frame (embedded in UL.ULMASTER) to contain the velocity plot
UL.OKFLAG - Boolean keeping track if the line is OK to use
UL.RBOK - A Checkbox button that sets UL.OKFLAG
UL.BLENDFLAG - Boolean keeping track if the line is blended
UL.RBBLEND - A checkbox button that sets UL.BLENDFLAG
UL.UPPERFLAG - Boolean keeping track if the line should be classified as upper limit
UL.RBUPPER - A checkbox that sets UL.UPPERFLAG
UL.LOWERFLAG - Boolean keeping track if the line is a lower limit
UL.RBLOWER - A Checkbox that sets UL.LOWERFLAG
UL.COLOUR - String of matplotlib colour to flag the feature with on main LINEFINDER display window
UL.NOTES - The string of notes associated with the absorption feature
UL.VMIN - The minimum (blueward) velocity tto start absorption feature
UL.VMAX - The maximum (redward) velocity to end absorption feature
UL.FIG - Matplotlib figure (embedded in UL.ULFRAME) to diosplay velocity profile of line
UL.AX - Matplotlib axes instance of velocity profile
UL.VMINLINE - The vertical line object to signify UL.VMIN on UL.AX plot
UL.VMAXLINE - The vertical line object to signify UL.VMAX on UL.AX plot
UL.CANVAS - The TKinter canvas in UL.ULFRAME for displaying plots.
UL.DRAWVMIN(REDRAW=TRUE) - (Re)draws the UL.VMIN line (UL.VMINLINE) on the UL.AX plot.
Set REDRAW=FALSE if it is the first instance.
UL.DRAWVMAX(REDRAW=TRUE) - (Re)draws the UL.VMAX line (UL.VMAXLINE) on the UL.AX plot.
Set REDRAW=FALSE if it is the first instance.
UL.ONSETVELS(EVENT) - Takes the mouse click event, and gets the x-coordinate (velocity).
If the left/right mouse button, UL.ONSETVELS fills in the entry for UL.VMIN/UL.VMAX
value, and redraws the UL.VMINLINE/UL.VMAXLINE (respectively)
UL.ONSAVEBUTTON - Takes all the information (flags, notes, colours, velocity limits) for the
absrotpion profile, saves it to UL.LOG, and destroys the UPDATELOG widget.
NOTES:
-The left mouse button shoudl set UL.VMIN on the window
-The right mouse button sets UL.VMAX.
-The flag coding is:
0 - No good/skip
1 - OK (i.e. use the line)
2 - Blend
4 - Upper Limit/Non-detection
8 - Lower Limit/Saturated
"""
def __init__(self,log, z,ion,line,wvl, iwvlngth, ispectrum):
#Display the tutorial message for how to use UPDATELOG
if usetutorial: tkMessageBox.showinfo("Help Message", "Input parameters associated with the absroption line."+ \
"Left/right clicking will set velocity bounds of absroption feature.")
#Set the input information as attributes
self.log=log
self.zstr='%.5f'%z
self.ion=ion
self.line=line
#Define the TK window
self.ULmaster=Tkinter.Toplevel()
#Create a frame for the velocity plot of the absroption line
self.ULFrame=Tkinter.Frame(self.ULmaster, width=80,height=40)
self.ULFrame.grid(row=0,column=0,columnspan=4)
#Set the name of the TK window
self.ULmaster.wm_title("Edit Log for z=%s (%s,%s)"%(self.zstr,self.ion,self.line))
#Initialize the variables in the log dictionary if they don't exist all ready
if (self.zstr,self.ion,self.line,'flag') not in self.log: self.log[self.zstr,self.ion,self.line,'flag']='0'
if (self.zstr,self.ion,self.line,'colour') not in self.log: self.log[self.zstr,self.ion,self.line,'colour']='k'
if (self.zstr,self.ion,self.line) not in self.log: self.log[self.zstr,self.ion,self.line]=''
if (self.zstr,self.ion,self.line,'vel') not in self.log: self.log[self.zstr,self.ion,self.line,'vel']='-50','50'
#Initialize different flags as boolean values
#Get the flag information from the log
flags=int(self.log[self.zstr,self.ion,self.line,'flag'])
#If flag contains an 8, it is a lower limit
initLower=(flags>=8)
#remove the 8 from the flag if necessary
if initLower: flags-=8
#If flag contains 4, it is an upper limit (remove if necessary)
initUpper=(flags>=4)
if initUpper: flags-=4
#If flag contains a 2, it is a blend (remove again...)
initBlend=(flags>=2)
if initBlend: flags-=2
#If flag is 1, it is OK to use!
initOK=(flags==1)
#Set up radio buttons for flags:
#Line is OK (i.e. good to use)
self.OKFlag=Tkinter.BooleanVar()
self.OKFlag.set(initOK)
self.RBOK=Tkinter.Checkbutton(self.ULmaster,text='Use line',variable=self.OKFlag,state='active')
self.RBOK.grid(column=0,row=1)
#Line is blended
self.BlendFlag=Tkinter.BooleanVar()
self.BlendFlag.set(initBlend)
self.RBBlend=Tkinter.Checkbutton(self.ULmaster,text='Blend',variable=self.BlendFlag,state='active')
self.RBBlend.grid(column=1,row=1)
#Line should be used as an upper limit
self.UpperFlag=Tkinter.BooleanVar()
self.UpperFlag.set(initUpper)
self.RBUpper=Tkinter.Checkbutton(self.ULmaster,text='Upper limit/non-detection',variable=self.UpperFlag,state='active')
self.RBUpper.grid(column=2,row=1)
#Line should be used as a lower limit
self.LowerFlag=Tkinter.BooleanVar()
self.LowerFlag.set(initLower)
self.RBLower=Tkinter.Checkbutton(self.ULmaster,text='Lower limit/saturated',variable=self.LowerFlag,state='active')
self.RBLower.grid(column=3,row=1)
#Set-up drop down menu for colour coding
labelCol=Tkinter.StringVar()#Name of string in label
labelcol=Tkinter.Label(self.ULmaster,textvariable=labelCol,\
anchor="w",fg="black")
labelcol.grid(column=0, row=2, columnspan=2, sticky='EW')
labelCol.set(u"Colour for display:")#Initial value of Variable
#SELF.COLOUR will be the colour of the line
self.Colour=Tkinter.StringVar()
self.Colour.set(self.log[self.zstr,self.ion,self.line,'colour'])
CMenu=apply(Tkinter.OptionMenu, (self.ULmaster,self.Colour,)+tuple(mplcolours))
CMenu.grid(column=2,row=2,columnspan=2,sticky='EW')
#Set-up field for entering comments on the line profile
labelNote=Tkinter.StringVar()#Name of string in label
labelnote=Tkinter.Label(self.ULmaster,textvariable=labelNote,\
anchor="w",fg="black")
labelnote.grid(column=0, row=3, sticky='EW')
labelNote.set(u"Comments:")#Initial value of Variable
#SELF.NOTES will have the notes for the line
self.Notes=Tkinter.StringVar()
entrynote=Tkinter.Entry(self.ULmaster,textvariable=self.Notes)
entrynote.grid(column=1,row=3,columnspan=3,sticky='EW')
self.Notes.set(self.log[self.zstr,self.ion,self.line])
#Set-up field for entering bounding velocities of absorption
labelVmin=Tkinter.StringVar()#Name of string in label
labelvmin=Tkinter.Label(self.ULmaster,textvariable=labelVmin,\
anchor="w",fg="black")
labelvmin.grid(column=0, row=4,sticky='EW')
labelVmin.set(u"Blue velocity bound:")#Initial value of Variable
self.Vmin=Tkinter.DoubleVar()
entryvmin=Tkinter.Entry(self.ULmaster,textvariable=self.Vmin)
entryvmin.grid(column=1,row=4,sticky='EW')
self.Vmin.set(float(self.log[self.zstr,self.ion,self.line,'vel'][0]))
#Set-up field for entering bounding velocities of absorption
labelVmax=Tkinter.StringVar()#Name of string in label
labelvmax=Tkinter.Label(self.ULmaster,textvariable=labelVmax,\
anchor="w",fg="black")
labelvmax.grid(column=2, row=4,sticky='EW')
labelVmax.set(u"Red velocity bound:")#Initial value of Variable
self.Vmax=Tkinter.DoubleVar()
entryvmax=Tkinter.Entry(self.ULmaster,textvariable=self.Vmax)
entryvmax.grid(column=3,row=4,sticky='EW')
self.Vmax.set(float(self.log[self.zstr,self.ion,self.line,'vel'][1]))
#Set up the save and exit button
SaveQuitButton=Tkinter.Button(self.ULmaster,text="Save Log & Exit", command=self.onSaveButton)
SaveQuitButton.grid(column=0,row=5,columnspan=4, sticky='EW')
#Set up Plot window
#Velocity limit fields
vmin=-1000.0
vmax=1000.0
#WVL should be Redshifted
wvl=wvl*(1.0+z)
#get velocity profile of the line from the spectrum
tmpvel,tmpspec=velspec(wvl, iwvlngth, vmin,vmax, ispectrum)
self.fig=plt.figure(figsize=(6,6))
self.fig.subplots_adjust(hspace=2.0,wspace=2.0)
self.ax=plt.subplot(1,1,1)
self.ax.plot(tmpvel,tmpspec,'k',drawstyle='steps')
self.ax.set_xlabel(r'Relative velocity (km s$^{-1}$)')
self.ax.set_ylabel(r'Relative intensity')
self.Canvas=FigureCanvasTkAgg(self.fig,self.ULFrame)
self.Canvas._tkcanvas.grid(row=0,column=0)
self.DrawVmin(redraw=False)
self.DrawVmax(redraw=False)
self.fig.canvas.mpl_connect("button_press_event",self.OnSetVels)
self.Canvas.start_event_loop(0)
#Functions to draw velocity limits on UL.AX instance. One is for each limit
def DrawVmin(self,redraw=True):
#REDRAW defines whether the line has to be redrawn. If not, it needs to create UL.VMINLINE
ymin,ymax=self.ax.get_ylim()
if redraw: self.ax.lines.remove(self.vminline[0])
self.vminline=self.ax.plot([self.Vmin.get(),self.Vmin.get()],[ymin,ymax],'--r')
self.Canvas.draw()
def DrawVmax(self,redraw=True):
#REDRAW defines whether the line has to be redrawn. If not, it needs to create UL.VMAXLINE
ymin,ymax=self.ax.get_ylim()
if redraw: self.ax.lines.remove(self.vmaxline[0])
self.vmaxline=self.ax.plot([self.Vmax.get(),self.Vmax.get()],[ymin,ymax],'--r')
self.Canvas.draw()
def OnSetVels(self,event):
#set UL.Vmin and UL.Vmax based on left/right mouse clicks
#Also redraw the velocity limit lines on UL.AX
vel=event.xdata
ymin,ymax=self.ax.get_ylim()
if event.button==1:
self.Vmin.set(vel)
self.DrawVmin(redraw=True)
elif event.button==3:
self.Vmax.set(vel)
self.DrawVmax(redraw=True)
def onSaveButton(self):
#Saves the current values in the GUI to the log file.
#Determine flag based on true/false values
flag=1*int(self.OKFlag.get())+2*int(self.BlendFlag.get())+4*int(self.UpperFlag.get())+8*int(self.LowerFlag.get())
#Save information to the LOG
self.log[self.zstr,self.ion,self.line,'flag']=str(flag)
self.log[self.zstr,self.ion,self.line,'vel']=str(self.Vmin.get()),str(self.Vmax.get())
self.log[self.zstr,self.ion,self.line]=self.Notes.get()
self.log[self.zstr,self.ion,self.line,'colour']=self.Colour.get()
#Destroy the widget
self.Canvas.stop_event_loop()
self.ULmaster.destroy()
#Quit UPDATELOG
return
def LoadLineList(file):
"""
LOADLINELIST - Loads the supplied linelist into a dictionary for TB_LINEFINDER
Call - LLIST=LoadLineList(FILE)
INPUT: FILE - The input ASCII file with the linelist for identification.
Each line in the file must contain information on each spectral line
of interest in the format (whitespace delimited):
REST_WAVELENGTH ION SHORT_WL OSCILLATOR_STRENGTH
REST_WAVELENGTH - The rest wavelength of the spectral line (e.g. 1215.6701)
ION - The species (including ionization state) of the spectral line (e.g. HI)
SHORT_WL - A short identifier of the wavelength (e.g. 1215)
Needed to descrimante between absorption lines of a given ION
OSCILLATOR_STRENGTH - The oscillator strength of the feature
This is only used for display purposes. (e.g. 0.4164)
EXAMPLE:
1215.6701 HI 1215 0.4164
If Ly-alpha not present, LOADLINELIST will add it by default
MUST CONTAIN THE SAME NUMBER OF COLUMNS IN EACH LINE (unless commented)
Can use '#' at the start of any line to comment
OUTPUT: LLIST - A dictionary with the linelist information. LLIST is structured as follows:
LLIST['ions'] - A list of all the ion species in the linelist (ION column)
LLIST['lines'] - A dictionary (keyed by an ION in LLIST['ions']) of all the
SHORT_WL identifiers for a given ION
LLIST['lines'][ION] is a list of all the SHORT_WL for a given ION
LLIST[ION,SHORT_WL] - A tuple with (WAVELENGTH,OSCILLATOR_STRENGTH) for
the spectral line identified ION,SHORT_WL
NOTES
For simplicity, SHORT_WL is abbreviated LINE in the code.
Will add Ly-alpha (HI 1215) by default if not present in FILE
"""
#Read in FILE.
#FILE in format: wvl ION lineID fval
data=np.genfromtxt(file,comments='#',dtype=type('str'))
#Create LLIST, the linelist dictionary
llist={}
#Create the list of ions and lines
llist['ions']=[]
llist['lines']={}
#For each line in FILE
for ii in range(len(data)):
ion=data[ii][1]
line=data[ii][2]
#Convert WAVELENGTH and OSCILLATOR_STRENGTH to a float
wvl=float(data[ii][0])#WAVELENGTH
f=float(data[ii][3])#OSCILLATOR_STRENGTH
#Save tuple to list
llist[ion,line]=wvl,f
#Check to see if ION or LINE (aka SHORT_WL) are in LLIST
#'ions' or 'lines' lists. If not, add them.
if ion not in llist['ions']:
llist['ions'].append(ion)
llist['lines'][ion]=[]
if line not in llist['lines'][ion]: llist['lines'][ion].append(line)
#Line list requires Ly-alpha. Add to LLIST if not present.
if ('HI','1215') not in llist:
llist['HI','1215']=1215.6701,0.41640
#REturn LLIST
return llist
class LineAdder:
"""
CLASS LINEADDER - The graphical interface for checking all spectral lines in
linelist (LLIST dictionary) visually, selecting which spectral
lines to add to the logfile (LOG dictionary). This window contains
a 3-column list of every available spectral line as a Checkbutton
for selecting which lines the user wants to add to the LOG dictionary
Call - LA=LineAdder(RADIOLIST,VELPLOTWIN)
INPUTS: RADIOLIST - A list of all the spectral lines available to generate
the Checkbuttons for selection. Each element in RADIOLIST
is a tuple of (ION,LINE) (the spectrl line key in LLIST dictionary)
VELPLOTWIN - The TKinter Window that will contain the widget for selecting lines
ATTRIBUTES:
LA.ADDLINES - A dictionary with all the spectral lines selected by the Checkbuttons.
ADDLINES is keyed by every element in RADIOLIST. The value is either TRUE/FALSE
depending if the corresponding Checkbutton was clicked (TRUE) or not (FALSE)
LA.MASTER - The master TKinter window (VELPLOTWIN)
LA.POPUP - The Frame of the TKinter window (i.e. the popup menu)
LA.VARLIST - A dictionary with the variables that contain whether a given
Checkbutton has been pressed or not. VARLIST is keyed by each element
in RADIOLIST, and is a TKINTER.BOOLEANVAR.
LA.SAVEBUTTON - THe widget button for ending the widget to enable saving
information to LOG dictionary.
LA.CLOSELAMENU - A function to close VELPLOTWIN and return the class stuff
back to the place where LINEADDER was called.
NOTES
THe only attribute that is really needed is LA.ADDLINES. The rest is just
for internal use of LINEADDER.
The grid of Checkbuttons will have the same layout as the figure showing
the velocity plots corresponding to each Checkbutton.
On pressing the SAVEBUTTON, VELPLOTWIN will be destroyed.
"""
#Intialize the class when called
def __init__(self,radiolist,VelPlotWin):
if debug: print "Initialize LineAdder Function"
#Define ADDLINES and populate each spectral line key
#from RADIOLIST with FALSE value (i.e. do not add line)
self.addlines={}
for key in radiolist:self.addlines[key]=False
#Define the parent window, and set up Tkinter frame
self.master=VelPlotWin
self.popup = Tkinter.Frame(self.master)
self.popup.grid()
self.master.title('LineAdder')
#Generate list of lines and radio buttons
self.varlist={}
#ROW is the row number in the LA.POPUP grid
row=1
#NCOL is the number of columns (set to 3) of Checkbuttons
ncol=3
#A dummy variable to keep track of which column the next
#Checkbutton should occupy
colnum=0
#FOr each spectral line key, create a Checkbutton
#and the associated Tkinter variable for that button.
for key in radiolist:
self.varlist[key]=Tkinter.BooleanVar()
self.varlist[key].set(self.addlines[key])
rb=Tkinter.Checkbutton(self.popup,text='%s (%s)'%key,\
variable=self.varlist[key])
rb.grid(column=colnum,row=row,sticky='EW')
colnum+=1#Increment COLNUM for next Checkbutton
#If COLNUM is at the maximum number of columns,
#increment ROW and reset COLNUM
if colnum==ncol:
colnum=0
row+=1
#If the row does not have enough buttons to fill it, increment
#ROW
if colnum>0: row+=1
#Define the "Save to Log" button for exiting the VELPLOTWIN window
#This will run the function CLOSELAMENU (below)
self.savebutton=Tkinter.Button(self.popup,text="Save to Log", command=self.closeLAMenu)#Runs OnFitButton function when clicked
self.savebutton.grid(column=0,row=row,columnspan=ncol,sticky='EW')
#Tell VELPLOTWIN to wait until window closes.
self.popup.wait_window()
#Function to close the LineAdder menu (i.e. VELPLOTWIN)
def closeLAMenu(self):
#Obtain the value of each spectral line Checkbutton
for key in self.varlist:
val=self.varlist[key].get()
#If the value is 1 (i.e. on), update LA.ADDLINES
#to TRUE for the given spectral line key.
if val==1:
self.addlines[key]=True
if debug: print "LineAdder: Closing Menu"
#Destroy the TKINTER window VELPLOTWIN and it's frame.
self.popup.destroy()
self.master.destroy()
return
def VelPlots(log,z,llist,fits,VelPlotWin,VPfig,VelFig):
"""
VELPLOTS - The function that plots all lines for a provided redshift
within provided linelist LLIST as a velocity profile. It will
then spawn a Tkinter window to select the spectral lines to
add to the LOG dictionary using the LINEADDER
class (defined above). Upon line selection, the user will be queryed
about the COLOUR,FLAG, VMIN/VMAX and NOTE for each spectral line added
to the log with the GETUSERINPUT.
Call - LOG=VelPlots(LOG,Z,LLIST,FITS,VELPLOTWIN,VPFIG, VELFIG)
INPUTS: LOG - The LOG dictionary defined from READLOG with the
spectral information
Z - The redshift of the system of interest (float)
LLIST - The linelist dictionary LLIST from input linelist file
FITS - The name of the input spectrum ASCII file to be plotted
VELPLOTWIN - The TKinter window that contains the matplotlib Canvas
VELFIG - The matplotlib TKagg canvas for velocity profiles in VELPLOTWIN window
VPFIG - The matplotlib figure embedded in the VELFIG canvas
OUTPUT: LOG - The edited LOG dictionary inputted into VELPLOTS.
NOTES:
The layout of the VELPLOTWIN canvas should be in the same order
as the Checkbuttons that appear in the LINEADDER GUI window. It
will have 3 columns total, and as many rows as needed for each
spectral line within the wavelength range of the spectrum.
"""
#Load the spectrum from the ascii file with SPECFITS
#IWVLNGTH and ISPECTRUM are numpy arrays with the
#wavelength and flux of the spectrum.
iwvlngth, ispectrum=specfits(fits)
if debug: print "VelPlots Loaded spectrum"
#LKEYS will contain a list of all the spectral
#line keys within LLIST that are within the
#wavelength range of the spectrum.
lkeys=[]
#Sort the ions list alphabetically to put
#IONS in order
llist['ions'].sort()
#Loop through each ION/LINE combination in the linelist
#to identify which lines are covered by the spectrum.
#If that is true, add the LLIST key to LKEYS.
for ion in llist['ions']:
#Sort the lines for a given ION so they are in order
llist['lines'][ion].sort()
for line in llist['lines'][ion]:
#DOuble check ION/LINE is in LLIST
#If this fails, something is fundamentally flawed...
if (ion,line) in llist:
#Get the wavelength of the spectral line
wvl,f=llist[ion,line]
#Redshift the line....
wvl=wvl*(1.0+z)
if debug: print "Wavelength check: is %.3f within (%.3f,%.3f)"%(wvl,min(iwvlngth),max(iwvlngth))
#If redshifted line covered by spectrum, add to LKEYS
if wvl>min(iwvlngth) and wvl<max(iwvlngth):
if debug: print "Passed wavelngth check",ion,line
lkeys.append((ion,line))
#NCOL is the number of columns in the VELPLOTWIN velocity profile grid
ncol=3
#NROW is the number of rows in the VELPLOTWIN grid.
nrow=len(lkeys)/ncol+1
if debug: print "VelPlots panel Nrow,ncol,nkeys:", nrow,ncol,len(lkeys)
#Set the limits of the plotted velocity profile to be +/- 1000 km/s
vmin=-1000
vmax=1000
if debug: print "VelPlots Plotting lines:",lkeys
#RADIOLIST is the list of LLIST spectral lines that require
#Checkbuttons in the LINEADDER GUI window
radiolist=[]
#The axes instance variable to share with all other panels
shareax=None
#Loop through all spectral lines for plotting and plot them!
for ii in range(len(lkeys)):
#Get the spectral line key (ION,LINE) tuple
lkey=lkeys[ii]
#Grabd the wavelenght and oscilaltor strength of the line
wvl,f=llist[lkey]
#WVL should be Redshifted
wvl=wvl*(1.0+z)
#IND refers to the matplotlib subplot index for the
#velocity profile
ind=ii+1
#Get the spectrum slice within vmin/vmax of the spectrum
#using VELSPEC (centred at the redshifted wavelength WVL)
#TMPVEL is the velocity array, TMPSPEC is the corresponding flux
tmpvel,tmpspec=velspec(wvl, iwvlngth, vmin,vmax, ispectrum)
#Variable to contain axes instance for the subplot
ax=None
#To share the same zoom on all plotting windows, the first window
#must be identified (ii==0), while subsequent widnows must share
#the x/y axes (SHAREX/SHAREY)
if ii==0:
#Add a subplot to the VELPLOTWIN figure, call it AX
ax=VPfig.add_subplot(nrow,ncol,ind)
shareax=ax
else:
ax=VPfig.add_subplot(nrow,ncol,ind,sharex=shareax)
#Plot the velocity profile to AX
ax.plot(tmpvel,tmpspec,'k',drawstyle='steps')
#Add key to RADIOLIST
radiolist.append(lkey)
#GEt y-axis limits of the velocity profile, and modify
#YMIN to include and extra 10% of the original heaigh
ymin,ymax=ax.get_ylim()
ymin=ymin-0.1*(ymax-ymin)
#get the ION,LINE identifier from the spectral line key
ion,line=lkey
#Set the title of the subplot to the idetifier, and
#provide the oscillator strength for reference
title='%s %s\nf=%.5f'%(ion,line,f)
ax.set_title(title)
#Loop through the current LOG dictionary, and plot
#a vertical dashed line to inform the user if there
#is potentially another line from a different system.
#LZ is the redshift of a system in the LOG allready
for lz in log['zs']:
#LION is the ion in the log
for lion in log['ions']:
#LLINE is the line identifier for LION
for lline in log['lines'][lion]:
#If the log has the key (LZ,LION,LLINE) and (LION,LLINE) are in the linelist LLIST
if (lion,lline) in llist and (lz,lion,lline) in log:
#Get the wavelength of the LION,LLINE
#And redshift it by LZ
wl,f=llist[lion,lline]
#Get the velocity of this line
#with respect to the the spectral
#Line being plotted.
vel=(wl*(1.0+float(lz))-wvl)/wvl*2.998E5
#If it's ALSO within the velocity
#profile of the spectral line plotted
if vel>vmin and vel<vmax:
#Plot a vertical dashed line, and add
#a label to inform the user of the contaminating
#line.
if debug: 'VelPlot Adding Log redshift',lz
col=log[lz,lion,lline,'colour']
ax.plot([vel,vel],[ymin,ymax],'--'+col, linewidth=3)
fval='%.5f'%f
label='z=%s\n%s %s'%(lz,lion,lline)
ax.text(vel,ymax,label,color=col,va='top',ha='left',rotation='vertical')
#Set the subplot velocity limits to the velocity range specified by VMIN/VMAX
ax.set_xlim(vmin,vmax)
#Update the VELFIG canvas
VelFig.draw()
if debug: print "VelPlots RadioList:", radiolist
#Use the LINEADDER class to generate the GUI window to select lines
#for adding to LOG
lineadder=LineAdder(radiolist,VelPlotWin)
#ADDLINES is a dicitionary keyed by the elements of RADIOLIST.
#Each key corresponds to whether or not the line should be added
#(true if add, false if not)
addlines=lineadder.addlines
if debug: print "VelPlots addlines:", addlines
#COnvert the redshift to a string for the log. Should be 5 decimcal places.
zstr='%.5f'%z
#Loop through each spectral line within ADDLINES
for key in addlines:
#If the user selected it (i.e. value==True)
if addlines[key]==True:
#Check if that system allready exists. If not
#add ZSTR to the list of redshifts in LOG['zs']