forked from obscode/snpy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsn.py
executable file
·2053 lines (1805 loc) · 81.4 KB
/
sn.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/env python
import sys,string,os
import warnings
have_sql = 1
import sqlmod
if 'SQLSERVER' in os.environ:
sqlmod.default_sql = sqlmod.__dict__['sql_'+os.environ['SQLSERVER']]()
else:
sqlmod.default_sql = sqlmod.sql_local()
have_sql = sqlmod.have_sql
try:
import snemcee
except ImportError:
snemcee = None
try:
import triangle
except ImportError:
triangle=None
import types
import time
import plot_sne_mpl as plotmod
from lc import lc # the light-curve class
from numpy import * # Vectors
import ubertemp # a template class that contains these two
import kcorr # Code for generating k-corrections
import bolometric
import utils.IRSA_dust_getval as dust_getval
from utils import fit_poly # polynomial fitter
import scipy # Scientific python routines
linalg = scipy.linalg # Several linear algebra routines
from scipy.interpolate import interp1d
from utils import fit_spline # My Spline fitting routines
from filters import fset # filter definitions.
from filters import standards # standard SEDs
import mangle_spectrum # SN SED mangling routines
import pickle
import model
from utils.fit1dcurve import list_types,regularize
from version import __version__
# Some useful functions in other modules which the interactive user may want:
getSED = kcorr.get_SED
Robs = kcorr.R_obs
Ia_w,Ia_f = getSED(0, 'H3')
Vega = standards.Vega.VegaB
BD17 = standards.Smith.bd17
class dict_def:
'''A class that acts like a dictionary, but if you ask for a key
that is not in the dict, it returns the key instead of raising
an exception.'''
def __init__(self, parent, dict={}):
self.dict = dict
self.parent = parent
def __getitem__(self, key):
if key in self.dict:
return self.dict[key]
else:
return key
def __setitem__(self, key, value):
self.dict[key] = value
def __delitem__(self, key):
self.dict.__delitem__(key)
def __contains__(self, key):
return self.dict.__contains__(key)
def __iter__(self):
return self.dict.__iter__()
def __str__(self):
ret = ""
for key in self.parent.data.keys():
ret += "%s -> %s, " % (key, self.__getitem__(key))
return ret
def __repr__(self):
return self.__str__()
def keys(self):
return self.dict.keys()
class sn(object):
'''This class is the heart of SNooPy. Create a supernova object by
calling the constructor with the name of the superova as the argument.
e.g::
In[1]: s = sn('SN1999T')
if the supernova is in the SQL database, its lightcurve data will be loaded
into its member data. Once the object is created, use its member data
and functions to do your work. Of course, you can have multiple
supernovae defined at the same time.
Args:
name (str): A SN name, or a filename containing data.
source (sqlbase): An instance of sqlmod.sqlbase class (for database access)
ra (float): degrees Right-ascention (J2000), of object
dec (float): degrees Declination (J2000) of object
z (float): heliocentric redshift of object
'''
def __init__(self, name, source=None, ra=None, dec=None, z=0):
'''Create the object. Only required parameter is the [name]. If this
is a new object, you can also specify [ra], [dec], and [z].'''
self.__dict__['data'] = {} # the photometric data, one for each band.
self.__dict__['model'] = model.EBV_model(self)
self.template_bands = ubertemp.template_bands
self.Version = __version__ # A version-stamp for when we upgrade
self.name = name
self.z = z # Redshift of the SN
self.ra = ra # Coordinates
self.decl = dec
self.filter_order = None # The order in which to plot the filters
self.xrange = None
self.yrange = None # Impose any global plotting ranges?
self.Rv_gal = 3.1 # galaxy
self.EBVgal = 0.0
self.fit_mag = False # fit in magnitude space?
self.restbands = dict_def(self, {}) # the band to which we are fitting for each band
self.ks = {} # k-corrections
self.ks_mask = {} # mask for k-corrections
self.ks_tck = {} # spline rep of k-corrections
self.Robs = {} # The observed R based on Rv and Ia spectrum
self.p = None
self.replot = 1 # Do we replot every time the fit finishes?
self.quiet = 1 # Have copious output?
if source is None:
if ra is None or dec is None:
if have_sql:
self.sql = sqlmod.default_sql
self.read_sql(self.name)
self._sql_read_time = time.gmtime()
else:
print "Warning: ra and/or decl not specified and no source specified."
print " setting ra, decl and z = 0"
self.ra = 0; self.decl = 0;
else:
self.ra = ra
self.decl = dec
else:
self.sql = source
self.read_sql(self.name)
#self.summary()
self.getEBVgal()
self.get_restbands() # based on z, assign rest-frame BVRI filters to
# data
self.k_version = 'H3'
def __getattr__(self, name):
if 'data' in self.__dict__:
if name in self.data:
return(self.data[name])
if name == 'zcmb':
return self.get_zcmb()
if name == 'Tmax':
if 'model' in self.__dict__:
if 'Tmax' in self.__dict__['model'].parameters:
if self.__dict__['model'].parameters['Tmax'] is not None:
return self.__dict__['model'].parameters['Tmax']
for f in self.data:
if self.restbands[f] == 'B':
if self.data[f].Tmax is not None:
return self.data[f].Tmax
return 0.0
if name == 'dm15':
if 'model' in self.__dict__:
if 'dm15' in self.__dict__['model'].parameters:
if self.__dict__['model'].parameters['dm15'] is not None:
return self.__dict__['model'].parameters['dm15']
for f in self.data:
if self.restbands[f] == 'B':
if self.data[f].dm15 is not None:
return self.data[f].dm15
return None
if name == 'parameters':
if 'model' in self.__dict__:
return self.__dict__['model'].parameters
else:
raise AttributeError, "Error, model not defined, so no paramters"
if name == 'errors':
if 'model' in self.__dict__:
return self.__dict__['model'].errors
else:
raise AttributeError, "Error, model not defined, so no errors"
if name in self.parameters:
return self.parameters[name]
if name.replace('e_','') in self.errors:
return self.errors[name.replace('e_','')]
if name == 'dm15':
if 'B' in self.data:
return getattr(self.data['B'], 'dm15', None)
else:
return None
if name == 'st':
return None
if name == 'redlaw':
return 'ccm'
raise AttributeError, "Error: attribute %s not defined" % (name)
def __setattr__(self, name, value):
if 'model' in self.__dict__:
if name in self.__dict__['model'].parameters:
self.__dict__['model'].parameters[name] = value
return
#if name == 'Rv_host'
self.__dict__[name] = value
def __eq__(self, other):
'''Check if the data in this instance is equal to the data in another.
Only checks raw data (light-curves).'''
res = set(self.data.keys()) == set(other.data.keys())
if not res:
return False
for f in self.data.keys():
res = res and (self.data[f] == other.data[f])
return res
def __ne__(self, other):
return not self.__eq__(other)
def choose_model(self, name, stype='dm15', **kwargs):
'''A convenience function for selecting a model from the model module.
[name] is the model to use. The model will be used when self.fit() is
called and will contain all the parameters and errors. Refer to
:class:`.model` for models and their parameters.
Args:
name (str): The name of the model (default: ``EBV_model``)
stype (str): the template parameter (``dm15`` or ``st``)
kwargs (dict): Any other arguments are sent to model's constructor
Returns:
None
'''
models = []
for item in model.__dict__:
obj = model.__dict__[item]
if type(obj) is types.ClassType:
if issubclass(obj, model.model):
models.append(item)
if name not in models:
st = "Not a valid model. Choose one of: "+str(models)
raise ValueError, st
self.model = model.__dict__[name](self, stype=stype, **kwargs)
self.template_bands = [b for b in self.model.rbs \
if b not in ['Bs','Vs','Rs','Is']]
def get_mag_table(self, bands=None, dt=0.5, outfile=None):
'''This routine returns a table of the photometry, where the data from
different filters are grouped according to day of observation. The
desired filters can be specified, otherwise all filters are
returned. When data is missing, a value of 99.9 is inserted.
Args:
bands (list): filters to include in the table
dt (flaot): controls how to group by time: observations
separated by less than ``dt`` in time are grouped.
outfile (str or open file): optinal file name for output
Returns:
dict: numpy arrays keyed by:
- ``MJD``: the epoch of observation
- [band]: the magnitude in filter [band]
- e_[band]: the error in [band]
Raises:
TypeError: the outfile is an incorrect type.
'''
if bands is None: bands = self.data.keys()
ret_data = {}
# First, we make a list of observation dates from all the bands.
times = [self.data[band].MJD for band in bands]
times = sort(concatenate(times))
# Eliminate repeating days:
gids = concatenate([[1], greater(absolute(times[0:-1] - times[1:]), dt)])
times = compress(gids, times)
ret_data['MJD'] = times
# Now loop through the bands and see where we need to fill in data
for band in bands:
gids = less(absolute(times[:,newaxis] - \
self.data[band].MJD[newaxis,:]), dt)
temp1 = 0.0*times + 99.9
temp2 = 0.0*times + 99.9
for i in range(len(gids)):
if sum(gids[i]) > 0:
temp1[i] = sum(self.data[band].mag*gids[i])/sum(gids[i])
temp2[i] = max(sqrt(sum(power(self.data[band].e_mag,2)*gids[i]))/sum(gids[i]),
sqrt(average(power(temp1[i] - \
compress(gids[i], self.data[band].mag),2))))
ret_data[band] = temp1
ret_data["e_"+band] = temp2
if outfile is not None:
if type(outfile) in types.StringTypes:
fp = open(outfile, 'w')
elif type(outfile) is types.FileType:
fp = outfile
else:
raise TypeError, "outfile must be a file name or file handle"
JDlen = len(str(int(ret_data['MJD']))) + 3
title = "MJD" + " "*(JDlen+2)
for b in bands: title += "%5s +/- " % b
print >> fp, title
format = "%%%d.2f " + "%5.2f %4.2f "*len(bands)
for i in range(len(ret_data['MJD'])):
data = []
for b in bands: data += [ret_data[b][i], ret_data['e_'+b][i]]
print >> fp, format % tuple(data)
fp.close()
return
else:
return(ret_data)
def lira(self, Bband, Vband, interpolate=0, tmin=30, tmax=90, plot=0,
dokcorr=True, kcorr=None):
'''Use the Lira Law to derive a color excess. [Bband] and [Vband]
should be whichever observed bands corresponds to restframe B and V,
respectively. The color excess is estimated to be the median of the
offset between the Lira
line and the data. The uncertainty is 1.49 times the median absolute
deviation of the offset data from the Lira line.
Args:
Bband (str): the observed filter corresponding to B-band
Vband (str): the observed filter corresponding to V-band
interpolate (bool): If true and a model (or interpolator) exists for
the observed filters, use it to interpolate
missing B or V data
tmin/tmax (flaot): range over which to fit Lira Law
plot (bool): If True, produce a plot with the fit.
dokcoor (bool): If True, k-correct the data before fitting
Returns:
4-tuple: (EBV, error, slope, eslope)
EBV: the E(B-V) color-excess
error: undertainty based on fit
slope: the late-time slope of the B-V color curve
eslope: the error in the late-time slope
'''
if kcorr is not None:
warnings.warn("Use of kcorr argument is deprecated. Use dokcorr "
" instead", stacklevel=2)
dokcorr=kcorr
# find V-maximum
t_maxes,maxes,e_maxes,restbands = self.get_rest_max([Vband])
Tmax = t_maxes[0]
t,BV,eBV,flag = self.get_color(Bband, Vband, dokcorr=dokcorr,
interp=interpolate)
# find all points that have data in both bands
gids = equal(flag, 0)
# If we're allowed to interpolate, add flag=1
if interpolate:
gids = gids + equal(flag, 1)
# Now apply a time criterion
gids = gids*greater_equal(t-Tmax, tmin)*less_equal(t-Tmax, tmax)
# Now check that we actually HAVE some data left
if not sometrue(gids):
raise RuntimeError, "Sorry, no data available between t=%f and t=%f" % (tmin,tmax)
# extract the data we want and convert to Vmax epochs
t2 = compress(gids, (t-Tmax)/(1+self.z))
BV2 = compress(gids, BV)
eBV2 = compress(gids, eBV)
# Next, solve for a linear fit (as diagnostic)
w = power(eBV2,-2)
c,ec = fit_poly.fitpoly(t2, BV2, w=w, k=1, x0=55.0)
rchisq = sum(power(BV2 - c[0] - c[1]*(t2-55.),2)*w)/(len(BV2) - 2)
ec = ec*sqrt(rchisq)
lira_BV = 0.732 - 0.0095*(t2 - 55.0)
#lira_EBV = stats.median(BV2 - lira_BV)
#e_lira_EBV = 1.49*stats.median(absolute(BV2 - lira_BV - lira_EBV))
w = power(eBV2,-2)
lira_EBV = sum((BV2 - lira_BV)*w)/sum(w)
e_lira_EBV = power(sum(w), -0.5)
print "Vmax occurred at %f" % (t_maxes[0])
print "Slope of (B-V) vs. t-Tvmax was %f(%f)" % (c[1], ec[1])
print "median E(B-V) = %f 1.49*mad(E(B-V)) = %f" % (lira_EBV, e_lira_EBV)
if absolute(c[1] + 0.0118) > 3*ec[1]:
print "WARNING: fit slope differs from Lira Law by more than three sigma"
if plot:
plotmod.plot_lira(t, t2, t_maxes, BV, eBV, BV2, tmin, tmax, c)
return (lira_EBV, e_lira_EBV, c[1], ec[1])
def get_rest_max(self, bands, deredden=0):
return self.get_max(bands, deredden=deredden, restframe=1)
def get_max(self, bands, restframe=0, deredden=0, use_model=0):
'''Get the maximum magnitude in [bands] based on the currently
defined model or spline fits.
Args:
bands (list): List of filters to fine maximum
restframe (bool): If True, apply k-corrections (default: False)
deredden (bool): If True, de-redden using Milky-Way color excess
and any model color excess, if defined
(default: False)
use_model (bool): If True and both a model and interpolator are
defined for a filter, use the model.
(default: False, i.e., use interpolator)
Returns:
4-tuple: (Tmax, Mmax, e_Mmax, rband)
Tmax: array of time-of-maximum, one for each of [bands]
Mamx: array of maximum magnitudes
e_Mmax: error
rband: list of rest-bands (if model was used to interpolate)
'''
if type(bands) in types.StringTypes:
bands = [bands]
scalar = True
else:
scalar = False
model_bands = [b for b in bands if b in self.model._fbands]
lc_model_bands = [b for b in bands if self.data[b].Mmax is not None]
if use_model:
lc_model_bands = [b for b in lc_model_bands if b not in model_bands]
for band in bands:
if band not in model_bands and band not in lc_model_bands:
raise ValueError, "Error: filter %s has not been fit " % band + \
"with a light-curve yet, so I cannot compute it's maximum"
N = len(bands)
result = (zeros(N, dtype=float32), zeros(N, dtype=float32),
zeros(N, dtype=float32), [""]*N)
if len(model_bands) > 0:
mod_result = self.model.get_max(model_bands, restframe=restframe,
deredden=deredden)
for i in range(N):
b = bands[i]
if b in model_bands:
mid = model_bands.index(b)
for j in range(4): result[j][i] = mod_result[j][mid]
if b in lc_model_bands:
if b in model_bands:
print "Warning: both model and spline fits present, using " +\
"spline values"
if restframe:
print "Warning: can't k-correct spline fits, you're getting " +\
"observed maxima!"
if deredden:
if band in self.Robs:
if type(self.Robs[band]) is type(()):
R = scipy.interpolate.splev(self.data[b].Tmax, self.Robs[band])
else:
R = self.parent.Robs[band]
else:
R = fset[band].R(wave=Ia_w, flux=Ia_f)
else:
R = 0
result[0][i] = self.data[b].Tmax
result[1][i] = self.data[b].Mmax - R*self.EBVgal
result[2][i] = self.data[b].e_Mmax
result[3][i] = b
if scalar:
return (result[0][0],result[1][0],result[2][0],result[3][0])
return result
def kcorr(self, bands=None, mbands=None, mangle=1, interp=1, use_model=0,
min_filter_sep=400, use_stretch=1, **mopts):
'''Compute the k-corrections for the named filters.
In order to get the best k-corrections possible,
we warp the SNIa SED (defined by self.k_version) to match the observed
photometry. Not all bands will be observed on the same day (or some
data may be less than reliable), so there are several arguments that
control how the warping is done.
Args:
bands (list or None): List of filters to k-correct or all if None
(default: None)
mbands (list of None): List of filters to use for mangling the SED.
(default: None: same as bands)
mangle (bool): If True, mangle (color-match) the SED to observed
colors. (default: True)
interp (bool): If True, interpolate missing colors. (default: True)
use_model (bool): If True, use a model to interpolate colors, if
one exists (default: False)
min_filter_sep (float): Filters whose effective wavelength are closer
than this are rejected. (Default: 400 A)
use_stretch (bool): If True, stretch the SED in time to match the
stretch/dm15 of the object. (Default: True)
mopts (dict): Any additional arguments are sent to the function
mangle_spectrum.mangle_spectrum2()
Returns:
None
Effects:
Upon successful completion, the following member variables will be
populated:
* self.ks: dictionary (indexed by filter) of k-corrections
* self.ks_mask: dictionary indicating valid k-corrections
* self.ks_tck: dictionary of spline coefficients for the k-corrections
(useful for interpolating the k-corrections).
* self.mopts: If mangling was used, contains the parameters of the
mangling function.
'''
if use_stretch and self.k_version != '91bg':
dm15 = getattr(self, 'dm15', None)
st = getattr(self, 'st', None)
if dm15 is None and st is None:
raise AttributeError, "Before you can k-correct with stretch, you"+\
" need to solve for dm15 or st, using either a model or LC fit"
if dm15 is None:
s = st
else:
if dm15 > 1.7:
print "Warning: dm15 > 1.7. Using this stretch on the Hsiao SED"
print " is not recommended. I'm setting stretch to 1.0. You"
print " might consider using the 91bg SED template."
s = kcorr.dm152s(1.7)
elif dm15 < 0.7:
s = kcorr.dm152s(0.7)
else:
s = kcorr.dm152s(dm15)
elif use_stretch and self.k_version == '91bg':
print "Warning: you asked for stretching the template SED, but"
print "you have selected the 91bg template. Setting stretch to 1.0."
s = 1.0
else:
s = 1.0
self.ks_s = s
if bands is None: bands = self.data.keys()
if mbands is None: mbands = [b for b in bands]
# Check the simple case:
if not mangle:
for band in bands:
x = self.data[band].MJD
# days since Bmax in the frame of the SN
days = (x - self.Tmax)/(1+self.z)/s
days = days.tolist()
self.ks[band],self.ks_mask[band] = map(array,kcorr.kcorr(days,
self.restbands[band], band, self.z, self.EBVgal, 0.0,
version=self.k_version))
self.ks_mask[band] = self.ks_mask[band].astype(bool)
#self.ks_tck[band] = scipy.interpolate.splrep(x, self.ks[band], k=1, s=0)
if len(x) > 1:
self.ks_tck[band] = fit_spline.make_spline(x, self.ks[band], x*0+1,
k=1, s=0, task=0, tmin=x.min(), anchor_dist=[0,0],
tmax=x.max())[0]
return
# Now see if we need to eliminate filters
eff_waves = array([fset[band].eff_wave(Ia_w,Ia_f) for band in mbands])
sids = argsort(eff_waves)
eff_waves = eff_waves[sids]
mbands = [mbands[sids[i]] for i in range(len(sids))]
dwaves = eff_waves[1:] - eff_waves[0:-1]
while sometrue(less(dwaves, min_filter_sep)):
bids = less(dwaves, min_filter_sep)
mbands = [mbands[i] for i in range(len(bids)) if not bids[i]] + mbands[-1:]
eff_waves = array([fset[band].eff_wave(Ia_w,Ia_f) for band in mbands])
dwaves = eff_waves[1:] - eff_waves[0:-1]
if not self.quiet: print "Mangling based on filters:", mbands
restbands = [self.restbands[band] for band in bands]
# now get the interpolated magnitudes all along the extent of the
# lightcurves.
mags = []
masks = []
res = self.get_mag_table(bands)
for band in mbands:
bids = greater(res[band], 90)
# find where we need to interpolate:
if use_model:
ev,eev,ma = self.model(band, res['MJD'])
mags.append(ev)
masks.append(ma)
elif interp:
ev,ma = self.data[band].eval(res['MJD'], t_tol=-1)
#mags.append(where(bids, ev, res[band]))
#masks.append(where(bids, ma, 1))
mags.append(ev)
masks.append(ma)
else:
mags.append(where(bids, 0.0, res[band]))
masks.append(1-bids)
mags = transpose(array(mags))
masks = transpose(array(masks))
# don't forget to convert to rest-frame epochs!
if self.Tmax is None:
raise AttributeError, \
"Error. self.Tmax must be set in oder to compute K-correctsions"
t = res['MJD'] - self.Tmax
if not sometrue(greater_equal(t, -19)*less(t, 70)):
raise RuntimeError, \
"Error: your epochs are all outside -20 < t < 70. Check self.Tmax"
kcorrs,mask,Rts,m_opts = kcorr.kcorr_mangle(t/(1+self.z)/s, bands,
mags, masks, restbands, self.z,
colorfilts=mbands, version=self.k_version, full_output=1, **mopts)
mask = greater(mask, 0)
kcorrs = array(kcorrs)
Rts = array(Rts)
# At this point, we have k-corrections for all dates in res['MDJ']:
# kcorrs[i,j] is kcorr for bands[j] on date res['MJD'][i]
# But there may be two observations separated by less than a day,
# in which case, they share the same k-correction. So figure that out
self.ks_mopts = {}
for i in range(len(bands)):
b = bands[i]
self.ks_tck[b] = fit_spline.make_spline(res['MJD'], kcorrs[:,i],
res['MJD']*0+1, k=1, s=0, task=0,
tmin=res['MJD'].min(), tmax = res['MJD'].max())[0]
self.ks[b] = scipy.interpolate.splev(self.data[b].MJD, self.ks_tck[b])
self.ks_mask[b] = array([mask[argmin(absolute(res['MJD'] - self.data[b].MJD[j])),i] \
for j in range(len(self.data[b].MJD))]).astype(bool)
self.ks_mopts[b] = [m_opts[argmin(absolute(res['MJD'] - self.data[b].MJD[j]))] \
for j in range(len(self.data[b].MJD))]
self.Robs[b] = fit_spline.make_spline(res['MJD'], Rts[:,i],
res['MJD']*0+1, k=1, s=0, task=0,
tmin=res['MJD'].min(), tmax=res['MJD'].max())[0]
def get_mangled_SED(self, band, i, normalize=True):
'''After the mangle_kcorr function has been run, you can use this
function to retrieve the mangled SED that was used to compute the
k-correction for the [i]'th day in [band]'s light-curve.
Args:
band (str): the refernece filter
i (int): index of filter [band]'s photometry
normalize (bool): If True, normalize the SED to observed flux.
Returns:
4-tuple: (wave, mflux, oflux, mfunc)
* wave: wavelength of SED in \AA
* mflux: mangled SED flux
* oflux: original (un-mangled) SED flux
* mfunc: mangling function evaluated at [wave]
'''
if 'ks_mopts' not in self.__dict__:
raise AttributeError, "Mangling info not found... try running self.kcorr()"
epoch = self.data[band].t[i]/(1+self.z)/self.ks_s
wave,flux = kcorr.get_SED(int(epoch), version=self.k_version)
man_flux = mangle_spectrum.apply_mangle(wave,flux, **self.ks_mopts[band][i])[0]
if not normalize:
return(wave*(1+self.z),man_flux,flux,man_flux/flux)
# Now compute the normalizing factor
num = power(10,-0.4*(self.data[band].magnitude[i] -\
self.data[band].filter.zp))
denom = fset[band].response(wave*(1+self.z), man_flux)
return(wave*(1+self.z),man_flux*num/denom,flux,man_flux/flux)
#def fit_color(self, band1, band2, **args):
# '''Fit a 1dcurve to the band1-band2 color. Can be an interactive fit
# as well.
# Args:
# band1,band2 (str): Filters comprising the color
# interp (bool): If True, interpolate missing data in either filter
# use_model (bool): If True and both a model and interpolator are
# defined for the filter, use the model to interpolate
# model_float (bool): If True, then re-fit the model to each filter
# independently (so each has independent Tmax)
# kcorr (bool): If True, return k-corrected color.
# method (str): Which interpolator to use. Use :meth`.list_types`
# to find out what is available.
# '''
# interp = args.get('interp', 1)
# use_model = args.get('use_model', 0)
# model_float = args.get('model_flot', 0)
# kcorr = args.get('kcorr', 0)
# method = args.get('method', 'spline2')
# t,c,ec,mask = self.get_color(band1, band2, interp, use_model, model_float,
# kcorr)
# if self.Tmax > 0:
# evt = arange(int(self.t[0]), int(self.t[-1]+1)*1.0 + self.Tmax)
# else:
# evt = arange(self.MJD[0], self.MJD[-1])*1.0
# tt,cc,ecc = fit1dcurve.regularize(t, c, ec)
# if getattr(self, 'cinterp', None) is None:
# self.cinterp = {}
# self.cinterp[(band1,band2)] = fit1dcurve.Interpolator(method,
# t, c, ec, less_equal(mask,2))
# if interactive:
def interp_table(self, bands, use_model=False, model_float=False,
dokcorr=False):
'''For a given set of bands, construct a table of contemporaneous
photometry, interpolating any missing data.
Args:
bands (list): Filters with which to construct the table
use_model (bool): If True, use the model to interpolate and
allow extrapolations based on the model
model_float (bool): If True, re-fit the model to each filter
independently (so each as independent Tmax)
dokcorr (bool): If True, return k-corrected values
Returns:
4-tuple: (MJD, mags, emags, flags)
MJD: the epoch of observations. This is the same for all filters,
so is a single 1D array
mags: list of 1D arrays of magnitudes, one for each input band
emags: list of 1D arrays of errors, one for each intput band
flags: list of 1D arrays, indicating how the value was derived:
* 0 - observation was measured
* 1 - value was based on an interpolation. Pretty safe
* 2 - value was based on extrapolation of the model. Meh.
* 4 - value was obtained by extrapolation. Not safe to use.
* 8 - k-corrections are not valid
'''
# First, get a table of all photometry:
data = self.get_mag_table(bands)
ms = []
ems = []
flags = []
for band in bands:
bids = greater(data[band], 90) # where we need to fill in
if dokcorr:
if band not in self.ks_tck:
raise RuntimeError, "No k-corrections defined for %s. Either set dokcorr=0 or run self.kcorr() first" % band
k = scipy.interpolate.splev(data['MJD'], self.ks_tck[band])
kflag = (less(data['MJD'],self.ks_tck[band][0][0]) &\
greater(data['MJD'], self.ks_tck[band][0][-1]))*8
else:
k = 0
kflag = 0
if not sometrue(bids):
# They all good, carry on.
ms.append(data[band]-k)
ems.append(data['e_'+band])
flags.append(bids*1 + kflag)
continue
# Need to interpolate from here on in
if use_model:
if model_float:
temp,etemp,mask = self.model(band, self.data[band].MJD)
weight = self.data[band].e_flux**2
weight = power(weight, -1)*mask*self.data[band].mask
# weighted mean offset
offset = sum((self.data[band].mag - temp)*weight)/\
sum(weight)
# error in mean offset (MAD)
doffset = median(absolute(self.data[band].mag - \
temp-offset))
else:
offset = 0
doffset = 0
temp,etemp,mask = self.model(band, data['MJD'])
ms.append(where(bids, temp+offset, data[band]) - k)
ems.append(where(bids, doffset, data['e_'+band]))
# IDs where valid interpolation is done
iids = mask & greater_equal(data['MJD'],self.data[band].MJD.min())\
& less_equal(data['MJD'], self.data[band].MJD.max())
# IDs where valid extrapolation is done
eids = mask & (less(data['MJD'],self.data[band].MJD.min())\
| greater(data['MJD'], self.data[band].MJD.max()))
flags.append(where(bids, iids*1+eids*2+(-mask)*4, bids)+kflag)
else:
interp = getattr(self.data[band], 'interp', None)
if interp is None:
raise ValueError, "Error: interpolator missing for %s" % band
temp,mask = self.data[band].interp(data['MJD'])
etemp = self.data[band].interp.error(data['MJD'])
ms.append(where(bids, temp, data[band])-k)
ems.append(where(bids, etemp, data['e_'+band]))
iids = mask
eids = -mask
flags.append(where(bids, iids*1 + eids*4, bids)+kflag)
return (data['MJD'], ms, ems, flags)
def get_color(self, band1, band2, interp=True, use_model=False,
model_float=False, dokcorr=False, kcorr=None):
'''return the observed SN color of [band1] - [band2].
Args:
band1,band2 (str): Filters comprising the color
interp (bool): If True, interpolate missing data in either filter
use_model (bool): If True and both a model and interpolator are
defined for the filter, use the model to interpolate
model_float (bool): If True, then re-fit the model to each filter
independently (so each has independent Tmax)
dokcorr (bool): If True, return k-corrected color.
Returns:
4-tuple: (MJD, color, error, flag)
MJD (float array):
epoch
color (float array):
observed color
error (float array):
uncertainty in color
flag (int array):
binary flag. Each of the following conditions are logically-or'ed:
* 0 - both bands measured at given epoch
* 1 - only one band measured
* 2 - extrapolation (based on template) needed, so reasonably safe
* 4 - interpolation invalid (extrapolation or what have you)
* 8 - One or both k-corrections invalid
'''
if kcorr is not None:
warnings.warn("Use of kcorr argument is deprecated. Use dokcorr "
" instead", stacklevel=2)
dokcorr=kcorr
it1 = getattr(self.data[band1], 'interp', None)
it2 = getattr(self.data[band2], 'interp', None)
mod1 = band1 in self.model._fbands
mod2 = band2 in self.model._fbands
if not interp:
# easy
data = self.get_mag_table([band1, band2])
gids = less(data[band1], 90) & less(data[band2], 90)
mjd = data['MJD'][gids]
col = (data[band1] - data[band2])[gids]
ecol = sqrt(data['e_'+band1]**2 + data['e_'+band2]**2)[gids]
flags = gids[gids]*0
if dokcorr:
if band1 not in self.ks:
raise ValueError, \
"band %s has no k-corrections, use self.kcorr()" % band1
if band2 not in self.ks:
raise ValueError, \
"band %s has no k-corrections, use self.kcorr()" % band2
# Now, we need to find indexes into each dataset that correspond
# to data['MJD'].
ids1 = searchsorted(self.data[band1].MJD, mjd)
ids2 = searchsorted(self.data[band2].MJD, mjd)
col = col - self.ks[band1][ids1] + self.ks[band2][ids2]
flags = flags + (-self.ks_mask[band1][ids1]*\
-self.ks_mask[band2][ids2])*8
return (mjd,col,ecol,flags)
elif use_model and mod1 and mod2:
MJD,ms,ems,flags = self.interp_table([band1,band2], use_model=True,
model_float=model_float, dokcorr=dokcorr)
elif not use_model and it1 and it2:
MJD,ms,ems,flags = self.interp_table([band1,band2], use_model=False,
model_float=model_float, dokcorr=dokcorr)
elif not use_model and mod1 and mod2:
MJD,ms,ems,flags = self.interp_table([band1,band2], use_model=True,
model_float=model_float, dokcorr=dokcorr)
else:
raise RutimeError, "You asked for interpolation, but there are no"\
" models or interpolators defined"
# We really don't care which one flags conditions
flags = bitwise_or(flags[0],flags[1])
return(MJD, ms[0]-ms[1], sqrt(ems[0]**2 + ems[1]**2), flags)
def getEBVgal(self, calibration='SF11'):
'''Gets the value of E(B-V) due to galactic extinction. The ra and decl
member variables must be set beforehand.
Args:
calibration (str): Which MW extionction calibraiton ('SF11' or
'SFD98')
Returns:
None
Effects:
self.EBVgal is set to Milky-Way color excess.
'''
if self.ra is not None and self.decl is not None:
self.EBVgal,mask = dust_getval.get_dust_RADEC(self.ra, self.decl,
calibration=calibration)
self.EBVgal = self.EBVgal[0]
else:
print "Error: need ra and dec to be defined, E(B-V)_gal not computed"
def get_zcmb(self):
'''Gets the CMB redshift from NED calculator and stores it locally.'''
zcmb = getattr(self, '_zcmb', None)
if zcmb is not None:
return zcmb
else:
import utils.zCMB
self._zcmb = utils.zCMB.z_cmb(self.z, self.ra, self.decl)
return self._zcmb
def get_distmod(self, cosmo='LambdaCDM', **kwargs):
'''Gets the distance modulus based on a given cosmology. Requires the
astropy module to work.
Args:
cosmo (str): The cosmology to use. The available cosmologies are
those in astropy.cosmology and kwargs can be set if the
cosmology takes them (see astropy.cosmology)
Default: LambdaCDM with Ho=74.3, O_m = 0.27, O_de = 0.73
kwargs (dict): Extra arguments for given cosmology
Returns:
float: mu, the distance modulus in magnitudes.
'''
try:
from astropy import cosmology
except:
raise ImportError, "Sorry, in order to compute distance modulus, " +\
"need the astropy module. Try 'pip install --no-deps astropy'"
try:
c = getattr(cosmology, cosmo)
except:
raise AttributeError, "Unknown cosmology specified. See astropy.cosmology"
if not isinstance(c, cosmology.core.Cosmology):
kwargs.setdefault('H0', 74.3)
kwargs.setdefault('Om0', 0.27)
kwargs.setdefault('Ode0', 0.73)
cos = c(**kwargs)
else:
cos = c
return cos.distmod(self.zcmb).value
def get_lb(self):
'''Computes the galactic coordinates of this objects.
Returns:
2-tuple: (l,b)
l: galactic longitude (in degrees)
b: galactic latitude (in degrees)
'''
import utils.radec2gal
return utils.radec2gal.radec2gal(self.ra, self.decl)
def summary(self, out=sys.stdout):
'''Get a quick summary of the data for this SN, along with fitted
parameters (if such exist).
Args:
out (str or open file): where to write the summary
Returns:
None
'''
print >> out, '-'*80
print >> out, "SN ",self.name
if self.z:
print >> out, "z = %.4f " % (self.z),
if getattr(self, '_zcmb', None) is not None:
print >> out, "zcmb = %.4f " % (self.zcmb),
if self.ra: print >> out, "ra=%9.5f " % (self.ra),
if self.decl: print >> out, "dec=%9.5f" % (self.decl),
print >> out, ""
print >> out, "Data in the following bands:",
for band in self.data: print >> out, band + ", ",
print >> out, ""
print >> out, "Fit results (if any):"
#for band in self.restbands:
# print >> out, " Observed %s fit to restbad %s" % (band, self.restbands[band])
systs = self.systematics()
for param in self.parameters: