def get_transient_model(model_type='built-in', **kwargs): """ """ model = _transient_loaders[model_type](**kwargs) if kwargs.get('host_extinction', False): model.add_effect(sncosmo.CCM89Dust(), 'host', 'rest') model.set(hostr_v=2.) model.add_effect(sncosmo.CCM89Dust(), 'mw', 'obs') return model
def __init__(self, metricName='ctmetric_lsst', magPrecision=0.01, limitingsnr=5., uniqueBlocks=False, **kwargs): """ """ cols = ['expMJD', 'filter', 'fiveSigmaDepth'] #,'fieldRA','fieldDec'] super(ctmetric_lsst, self).__init__(cols, metricName) self.metriccache = dict() self.ebvprecision = 0.01 self.limitingsnr = limitingsnr self.ccm = sncosmo.CCM89Dust() self.metricDtype = 'float' #survey args self.filterinfo = lsst_info.LSST.filterinfo #metric args self.magPrecision = magPrecision self.map = lsst.sims.photUtils.EBV.EbvMap() dotlocation = string.rfind(kwargs['lc.class'], '.') module_name = kwargs['lc.class'][0:dotlocation] class_name = kwargs['lc.class'][dotlocation + 1:] somemodule = importlib.import_module(module_name) self.lightcurve = getattr(somemodule, class_name)(**kwargs)
def setUp(self) -> None: self.model = SNModel(source=sncosmo_test_models.flatsource(), effects=[sncosmo.CCM89Dust()], effect_frames=['obs'], effect_names=['mw']) self.model.set(z=0.0001)
def fitting_z(self, sn_name): """SALT2 fit using SNCOSMO for the SN without => z free parameter of the fit """ self.read(sn_name) self.source = sncosmo.get_source('salt2', version='2.4') dust = sncosmo.CCM89Dust() self.model = sncosmo.Model(source=self.source, effects=[dust], effect_names=['mw'], effect_frames=['obs']) self.model.set(mwebv=self.mwebv) #self.model.set(c=0) try: results, fit_model = sncosmo.fit_lc(self.data, self.model, ['t0', 'x1', 'c', 'x0']) #res, fitted_model = sncosmo.fit_lc(self.data, self.model, ['t0','x1', 'x0']) #return res, fitted_model except: #except RuntimeError: print('Fail:', sn_name) results, fit_model = 'fit fail', np.nan return results, fit_model
def extinction(self, eb_v, dust_law, r_v=3.1): """ Given eb-v and r-v return the dust extinction in a particular BAND It can use: Cardelli dust law (CCM), ODonneal (OD94) Fitzpatrick (F99) KEEP IN MIND: _minwave = 909.09 _maxwave = 33333.33 """ if dust_law == 'OD94': dust = sncosmo.OD94Dust() elif dust_law == 'CCM': dust = sncosmo.CCM89Dust() elif dust_law == 'F99': dust = sncosmo.F99Dust() else: print('Add this dust law! I dont know it') dust.parameters = [eb_v, r_v] w = self.wave t = self.transmission ext = dust.propagate(w, t) correction_extinction = np.trapz((ext)[1:-1], w[1:-1]) / np.trapz( (t)[1:-1], w[1:-1]) return correction_extinction
def fitting(self, sn_name): """Fit the light curve for a SN (data as Astropy table) using the model SALT2 z and mwebv are setted, it returns the values of the SALT2 parameters and their errors. We plot the fit on the notebook. """ self.read(sn_name) self.source = sncosmo.get_source('salt2', version='2.4') dust = sncosmo.CCM89Dust() self.model = sncosmo.Model(source=self.source, effects=[dust], effect_names=['mw'], effect_frames=['obs']) self.model.set(mwebv=self.mwebv) self.model.set(z=self.zhl) #self.model.set(c=0) try: res, fitted_model = sncosmo.fit_lc(self.data, self.model, ['t0', 'x1', 'c', 'x0'], phase_range=(-15, 45)) #res, fitted_model = sncosmo.fit_lc(self.data, self.model, ['t0','x1', 'x0']) #return res, fitted_model except: #except RuntimeError: print('Fail:', sn_name) res, fitted_model = 'fit fail', np.nan return res, fitted_model
def test_error_for_bad_frame(self) -> None: """Test an error is raised for a band reference frame name""" model = SNModel(source='salt2') with self.assertRaises(ValueError): model.add_effect(effect=sncosmo.CCM89Dust(), frame='bad_frame_name', name='mw')
def test_free_effect_adds_z_parameter(self) -> None: """Test effects in the ``free`` frame of reference include an added redshift parameter""" effect_name = 'freeMW' model = SNModel(source='salt2') model.add_effect(effect=sncosmo.CCM89Dust(), frame='free', name=effect_name) self.assertIn(effect_name + 'z', model.param_names)
def setUp(self) -> None: # Same as the base sncosmo setup procedure, but using a ``SNModel`` # instance instead of ``sncosmo.Model`` self.model = SNModel(source=sncosmo_test_models.flatsource(), effects=[sncosmo.CCM89Dust()], effect_frames=['obs'], effect_names=['mw']) self.model.set(z=0.0001)
def model_(phase=0, wave=0, cos_theta=0, flux=0, viewingangle_dependece=True): '''phase, wave, cos_theta and flux are 1D arrays with the same length''' if viewingangle_dependece: source = AngularTimeSeriesSource(phase, wave, cos_theta, flux) else: source = sncosmo.TimeSeriesSource(phase, wave, flux) dust = sncosmo.CCM89Dust() return sncosmo.Model(source=source, effects=[dust, dust], effect_names=['host', 'MW'], effect_frames=['rest', 'obs'])
def create_SALT2_model(ts_data, meta_data, object_id_list): warnings.simplefilter("ignore") result_list = [] for object_id in object_id_list: if object_id is None: # for itertools.zip_longest method continue result_tmp = [object_id] photoz = meta_data.query( 'object_id == @object_id')["hostgal_photoz"].values[0] mwebv = meta_data.query('object_id == @object_id')["mwebv"].values[0] if photoz == 0: result_tmp.extend([np.nan, np.nan, np.nan, np.nan]) result_list.append(result_tmp) continue extract_obj = ts_data.query('object_id == @object_id') data = astropy.table.Table( extract_obj.values, names=['object_id', 'mjd', 'band', 'flux', 'flux_err', 'detected'], dtype=('str', 'float', 'str', 'float', 'float', 'str')) # model = sncosmo.Model(source='salt2') dust = sncosmo.CCM89Dust() model = sncosmo.Model(source='salt2-extended', effects=[dust, dust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) model.set(z=photoz, mwebv=mwebv, hostebv=0, hostr_v=3.1, mwr_v=3.1) # cutting data data_mask = model.bandoverlap(data["band"], z=photoz) data = data[data_mask] def objective(parameters): model.parameters[1:5] = parameters model_flux = model.bandflux(data['band'], data['mjd']) return np.sum(((data['flux'] - model_flux) / data['flux_err'])**2) start_parameters = [60000, 1e-5, 0., 0.] # t0, x0, x1, c bounds = [(extract_obj.mjd.min(), extract_obj.mjd.max()), (None, None), (None, None), (None, None)] parameters, val, info = fmin_l_bfgs_b(objective, start_parameters, bounds=bounds, approx_grad=True) result_tmp.extend(parameters) result_list.append(result_tmp) return result_list
def set_model(self, model): """ Set the transient model. If it does not have MW dust effect, the effect is added. """ if model.__class__ is not sncosmo.models.Model: raise TypeError("model must be sncosmo.model.Model") if "mwebv" not in model.param_names: model.add_effect(sncosmo.CCM89Dust(), 'mw', 'obs') self._properties["model"] = model
def calc_mb(**param_dict): """Calculates the apparent magnitude in the B-band based on the SALT2 parameters in the param_dict""" model = sncosmo.Model(source='salt2', effects=[sncosmo.CCM89Dust()], effect_names=['mw'], effect_frames=['obs']) model.set(**param_dict) try: mb = model.bandmag(band='bessellb', time=param_dict['t0'], magsys='ab') except: mb = np.nan return mb
def __init__(self, metricName='snmetric_lsst', filterNames=numpy.array(['u', 'g', 'r', 'i', 'z', 'y']), filterTargetMag=numpy.array([22, 22.5, 22.5, 22.5, 22.5, 22]), filterWave=numpy.array([375., 476., 621., 754., 870., 980.]), T0=30., Omega=1., zmax=.5, snr=50., tau=4., dt0=28., a=1., uniqueBlocks=False, **kwargs): """ """ cols = ['night', 'filter', 'fivesigma_modified', 'fieldRA', 'fieldDec'] super(snmetric_lsst, self).__init__(cols, metricName, **kwargs) self.longestTolerableGap = 15. self.metricDtype = 'float' #survey args self.filterinfo = dict() for name, mag, wave in zip(filterNames, filterTargetMag, filterWave): self.filterinfo[name] = dict() self.filterinfo[name]['targetMag'] = mag self.filterinfo[name]['wave'] = wave #self.filterNames = filterNames #self.filterTargetMag = filterTargetMag #self.filterWave=filterWave #metric args # for now these are all scalars though in principle could be filter-dependent self.T0 = T0 self.Omega = Omega self.zmax = zmax self.snr = snr self.tau = tau self.dt0 = dt0 self.a = a self.ccm = sncosmo.CCM89Dust() self.map = lsst.sims.photUtils.EBV.EbvMap() self.metric_calc = surveymetrics.snmetric.Metric.OneFieldBandMetric( self.Omega, self.zmax, self.snr, self.T0, self.tau, self.dt0, self.a)
def test_bandpass_type(): """Check that bandpass wavelength type is always float64, and that color laws work with them.""" dust = sncosmo.CCM89Dust() for dt in [np.int32, np.int64, np.float32]: wave = np.arange(4000., 5000., 20., dtype=dt) trans = np.ones_like(wave) band = sncosmo.Bandpass(wave, trans) assert band.wave.dtype == np.float64 # Ensure that it works with cython-based propagation effect. dust.propagate(band.wave, np.ones_like(wave))
def equivalentSNCosmoModel(self): """ returns an SNCosmo Model which is equivalent to SNObject """ snState = self.SNstate dust = sncosmo.CCM89Dust() sncosmoModel = sncosmo.Model(source=snState['ModelSource'], effects=[dust, dust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) sncosmoParams = self.sncosmoParamDict(snState, sncosmoModel) sncosmoParams['mwebv'] = snState['MWE(B-V)'] sncosmoModel.set(**sncosmoParams) return sncosmoModel
def fromSNState(cls, snState): """ creates an instance of SNObject with a state described by snstate. Parameters ---------- snState: Dictionary summarizing the state of SNObject Returns ------- Instance of SNObject class with attributes set by snstate Example ------- """ # Separate into SNCosmo parameters and SNObject parameters dust = sncosmo.CCM89Dust() sncosmoModel = sncosmo.Model(source=snState['ModelSource'], effects=[dust, dust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) sncosmoParams = cls.sncosmoParamDict(snState, sncosmoModel) # Now create the class cls = SNObject(source=snState['ModelSource']) # Set the SNObject coordinate properties # Have to be careful to not convert `None` type objects to degrees setdec, setra = False, False if snState['_ra'] is not None: ra = np.degrees(snState['_ra']) setra = True if snState['_dec'] is not None: dec = np.degrees(snState['_dec']) setdec = True if setdec and setra: cls.setCoords(ra, dec) # Set the SNcosmo parameters cls.set(**sncosmoParams) # Set the ebvofMW by hand cls.ebvofMW = snState['MWE(B-V)'] return cls
def lens_extinction(source='salt2-extended', zs=1.95, zl=0.338, Av=1, dust=sncosmo.CCM89Dust(), t=0): # the source makes no difference, dust has same effect on every model # the model as it would look like for obs at the lens plane w no dust model = sncosmo.Model(source=source) model.set(z=zs - zl) # the model as it would look for obs at lens w dust dust_model = sncosmo.Model(source=source, effects=[dust], effect_names=['mw'], effect_frames=['obs']) dust_model.set(z=zs - zl) r_v = 3.1 # AV/E(B-V) typical value for diffuse ISM ebv = Av / r_v dust_model.set(mwebv=ebv, mwr_v=r_v) """ I want to know how the dust dimmed f160w & f105w in our frame ie zl~0.338 further f160w ~ 16000 & f105w ~ 10500 (Angstrom), they have widths about 1500 (Angstrom) lens observer at these wavelengths/(1+zl) should correspond to the wavelengths for us I can get flux density at each of these filters eff wavelength w & wo dust the filters aren't terribly wide and throughputs are flat, will approximate mags using just eff wavelength that should get me an approximate vec shift (E(105-160),A160) I want w Av ~ 1 in the lens plane """ # the intrinsic flux densities for lens observer at the corresponding wavelengths to bands we see f160 = model.flux(t, [16000 / (1 + zl)]) f105 = model.flux(t, [10500 / (1 + zl)]) # dusty flux densities dustyf160 = dust_model.flux(t, [16000 / (1 + zl)]) dustyf105 = dust_model.flux(t, [10500 / (1 + zl)]) # what do the extinctions look like, A ~ mobs - m A160 = -2.5 * np.log10(dustyf160 / f160) A105 = -2.5 * np.log10(dustyf105 / f105) # my color mag is (x,y) ~ (f105w-f160w,f160w) # I want to show vector (E(105-160),A160) # color excess E = A105 - A160 return (E[0], A160[0])
def test_fit_lc_vs_snfit(): """Test fit_lc versus snfit result for one SN.""" # purposefully use CCM dust to match snfit model = sncosmo.Model(source='salt2', effects=[sncosmo.CCM89Dust()], effect_names=['mw'], effect_frames=['obs']) fname = join(dirname(__file__), "data", "lc-03D4ag.list") data = sncosmo.read_lc(fname, format='salt2', read_covmat=True, expand_bands=True) model.set(mwebv=data.meta['MWEBV'], z=data.meta['Z_HELIO']) result, fitted_model = sncosmo.fit_lc(data, model, ['t0', 'x0', 'x1', 'c'], bounds={ 'x1': (-3., 3.), 'c': (-0.4, 0.4) }, modelcov=True, phase_range=(-15., 45.), wave_range=(3000., 7000.), warn=False, verbose=False) print(result) assert result.ndof == 25 assert result.nfit == 3 assert_allclose(fitted_model['t0'], 52830.9313, atol=0.01, rtol=0.) assert_allclose(fitted_model['x0'], 5.6578663e-05, atol=0., rtol=0.005) assert_allclose(fitted_model['x1'], 0.937399344, atol=0.005, rtol=0.) assert_allclose(fitted_model['c'], -0.0851965244, atol=0.001, rtol=0.) # errors assert_allclose(result.errors['t0'], 0.0955792638, atol=0., rtol=0.01) assert_allclose(result.errors['x0'], 1.52745001e-06, atol=0., rtol=0.01) assert_allclose(result.errors['x1'], 0.104657847, atol=0., rtol=0.01) assert_allclose(result.errors['c'], 0.0234763446, atol=0., rtol=0.01)
def run(self, dataSlice, slicePoint=None): #get ebv of this slice # dec=numpy.maximum(dataSlice['fieldDec'][0],-numpy.pi/2) # co = coord.ICRS(ra=dataSlice['fieldRA'][0], dec=dec, unit=(u.rad, u.rad)) # co = coord.ICRS(ra=slicePoint['ra'], dec=slicePoint['dec'], unit=(u.rad, u.rad)) ebv = ctmetric_lsst.ebvObject.calculateEbv(ra=[slicePoint['ra']], dec=[slicePoint['dec']]) # ebv=sncosmo.get_ebv_from_map(co, mapdir='../data',cache=True) # ebv=0. #check to see of metric is already available for this ebv label = round(ebv / self.ebvprecision) if label in self.metriccache: #if so use it print 'using old', ebv, label metric = self.metriccache[label] else: #if not create it print 'making new ', ebv, label #make outside Milky Way mag into dusted magnitude ccm = sncosmo.CCM89Dust() ccm.set(ebv=ebv) fn = lambda x, y: self.lightcurve.mag(x, y) - 2.5 * numpy.log10( ccm.propagate( numpy.array([lsst_info.LSST.filterinfo[y]['wave'] * 10]), numpy.array([1]))) metric = surveymetrics.ctmetric.ControlTimeMetric( fn, self.lightcurve.trange(), magPrecision=self.magPrecision) self.metriccache[label] = metric #convert limiting magnitude to threshold assuming sky limit m5col_ = dataSlice['fiveSigmaDepth'] mtarget = m5col_ - 2.5 * numpy.log10(self.limitingsnr / 5.) mjds = dataSlice['expMJD'] bands = dataSlice['filter'] #calculate return metric.calcControlTime(mtarget, mjds, bands)
def __init__(self, ra=None, dec=None, source='salt2-extended'): """ Instantiate object Parameters ---------- ra : float ra of the SN in degrees dec : float dec of the SN in degrees source : instance of `sncosmo.SALT2Source`, optional, defaults to using salt2-extended source class to define the model """ dust = sncosmo.CCM89Dust() sncosmo.Model.__init__(self, source=source, effects=[dust, dust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) # Current implementation of Model has a default value of mwebv = 0. # ie. no extinction, but this is not part of the API, so should not # depend on it, set explicitly in order to unextincted SED from # SNCosmo. We will use catsim extinction from `lsst.sims.photUtils`. self.ModelSource = source self.set(mwebv=0.) # self._ra, self._dec is initialized as None for cases where ra, dec # is not provided self._ra = None self._dec = None # ra, dec is input in degrees # If provided, set _ra, _dec in radians self._hascoords = True if dec is None: self._hascoords = False if ra is None: self._hascoords = False # Satisfied that coordinates provided are floats if self._hascoords: self.setCoords(ra, dec) # For values of ra, dec, the E(B-V) is calculated directly # from DustMaps self.lsstmwebv = EBVbase() self.ebvofMW = None if self._hascoords: self.mwEBVfromMaps() # Behavior of model outside temporal range : # if 'zero' then all fluxes outside the temporal range of the model # are set to 0. self._modelOutSideTemporalRange = 'zero' # SED will be rectified to 0. for negative values of SED if this # attribute is set to True self.rectifySED = True return
def snia(ax, band='p48g', timescale='efoldDecline', remove_nearby=True): """ Please contact me if you have any questions: [email protected] Sample from SNe Ia young sample observed in the ZTF extragalactic high-cadence partnership experiment. We use 121 ``normal'' SNe Ia from 2018 (including normal, 91T-like, and 91bg-like SNe). Light curves presented in Yao et al. (2019) Parameters: ----------- band: 'p48g': use g band ZTF data 'p48r': use r band ZTF data 'swope2::v', 'f555w', ...: use the SALT2 (Guy et al 2007) tool to estimate light curves in other bands timescale: 'efoldDecline': plot the rest-frame duration for the flux to drop from peak to 1/e*peak [other options TBD] remove_nearby [bool]: If True, remove two SNe with z < 0.02 (ZTF18aasdted and ZTF18abgmcmv), since their absolute peak luminosity are largely affected by the local group peculiar velocity. """ from snia_funcs import add_ZTFfilters, mylinear_fit cwd = os.getcwd() datafile = cwd[:-4] + 'data/snia/' + band + '_' + timescale + '.csv' tbfile = cwd[:-4] + 'data/snia/Yao2019_catalog.csv' try: os.stat(datafile) except: tb = pd.read_csv(tbfile) add_ZTFfilters() zp_temp = 26.325 Mpeaks = np.zeros(len(tb)) Mpeaks_unc = np.zeros(len(tb)) Tdeclines = np.zeros(len(tb)) for i in range(len(tb)): name = tb['name'].values[i] lcfile = cwd[:-4] + 'data/snia/lightcurve/' + name + '.csv' z = tb['z'].values[i] z_unc = tb['z_unc'].values[i] ebv = tb['E_B_V_SandF'].values[i] mytb = pd.read_csv(lcfile) lcband = mytb['band'].values Fratio = mytb['Fratio'].values Fratio_unc = mytb['Fratio_unc'].values zp = np.ones(len(mytb)) * zp_temp zpsys = ['ab'] * len(mytb) time = mytb['jdobs'].values lc = Table( data=[time, lcband, Fratio, Fratio_unc, zp, zpsys], names=['time', 'band', 'flux', 'fluxerr', 'zp', 'zpsys']) dust = sncosmo.CCM89Dust() model = sncosmo.Model(source='salt2', effects=[dust], effect_names=['mw'], effect_frames=['obs']) model.set(z=z, mwebv=ebv, mwr_v=3.1) res, fitted_model = sncosmo.fit_lc(lc, model, ['t0', 'x0', 'x1', 'c'], bounds={ 'x1': (-10., 10.), 'c': (-5., 5.) }) t0jd_B = res.parameters[1] # B band maximum light epoch t0jd_B_unc = res['errors']['t0'] ta = np.round(t0jd_B - 25, 2) + 0.01 tgrid = np.linspace(ta, ta + 75, 100 * 100 + 1) fitted_band = fitted_model.bandflux(band, tgrid, zp=zp_temp, zpsys='ab') # estimate peak epoch and apparent magnitude ixx = np.argsort(fitted_band) t_max = tgrid[ixx[-1]] fratio_max = fitted_band[ixx[-1]] appmag_max = -2.5 * np.log10(fratio_max) appmag_around = fitted_model.bandmag( band, 'ab', [t_max - t0jd_B_unc, t_max + t0jd_B_unc]) - zp_temp appmag_max_unc = (np.sum(appmag_around) - 2 * appmag_max) / 2. # estimate extinction corrected peak epoch apparent magnitude wave_eff = sncosmo.get_bandpass(band).wave_eff Aband = extinction.ccm89(np.array([wave_eff]), 3.1 * ebv, 3.1)[0] appmag0_max = appmag_max - Aband # estimate extinction corrected peak epoch absolute magnitude D = cosmo.luminosity_distance([z])[0].value * 1e+6 # in pc D_unc = ( cosmo.luminosity_distance([z + z_unc])[0].value - cosmo.luminosity_distance([z - z_unc])[0].value) / 2. * 1e+6 dis_mod = 5 * np.log10(D / 10) dis_mod_unc = 5 / np.log(10) / D * D_unc absmag0_max = appmag0_max - dis_mod absmag0_max_unc = np.sqrt(appmag_max_unc**2 + dis_mod_unc**2) Mpeaks[i] = absmag0_max Mpeaks_unc[i] = absmag0_max_unc # estimate the characteristic timescale if timescale == 'efoldDecline': fratio_end = fratio_max / np.e ind = np.argsort(abs(fitted_band - fratio_end)) ix_end = ind[ind > ixx[-1]][0] t_end = tgrid[ix_end] time_duration = t_end - t_max Tdeclines[i] = time_duration tbnew = Table(data=[ tb['name'].values, tb['z'].values, Mpeaks, Mpeaks_unc, Tdeclines ], names=['name', 'z', 'mag', 'mag_unc', 'timescale']) tbnew.write(datafile, overwrite=True) data = pd.read_csv(datafile) data = data[data.z > 0.02] x = data['timescale'].values / (1 + data['z'].values) y = data['mag'].values y_err = data['mag_unc'].values # plt.figure(figsize=(12, 8)) # ax = plt.subplot(111) # plot individual data points ax.errorbar(x, y, y_err, fmt='.k') # plot the grey contour slope, e_slope, intercept = mylinear_fit(x, y, y_err, npar=2) nstd = 5. # to draw 5-sigma intervals slope_up = slope + nstd * e_slope slope_dw = slope - nstd * e_slope x_fit = np.linspace(min(x) - 0.5, max(x) + 0.5, 100) # fit = slope * x_fit+ intercept fit_up = slope_up * x_fit + intercept fit_dw = slope_dw * x_fit + intercept ax.fill_between(x_fit, fit_up, fit_dw, color='grey', alpha=.25, label="5-sigma interval")
def __init__(self,survey_fields=None, simlib_file=None,simlib_obs_sets=None, c_pdf='Normal',\ c_params=[-0.05,0.2],x1_pdf='Normal',x1_params=[0.5,1.0],\ t0min = 56525.0,t0max=57070.0,NumSN = 500,minNumEpochs = 5,minNumFilters = 3, minSNR = 4.0,\ cosmo=None,alpha=0.14,beta=3.2,deltaM=0.0,zp_off=[0.0,0.0,0.0,0.0]): '''Input: survey_fields:dict, survey fields to generate z dist, values should be [area,zmin,zmax,dndz func,time ] simlib_file: str, snana simlib simlib_obs_sets= observation library c_pdf and x1_pdf: str, either 'Normal' or 'SkewNormal' c_params and x1_params: list, hyperparams for 'Normal' [mean,var] or 'SkewNormal' [mean,var,skew] t0min: float t0max: float NumSN: int, number of SN to simulate and fit minNumEpochs: int, minNumFilters: int, minSNR: int, selection cut: require minNumEpochs in at least minNumFilters with SNR>minSNR ''' self.t0min = t0min self.t0max = t0max self.NumSN = NumSN self.minNumEpochs = minNumEpochs self.minNumFilters = minNumFilters self.minSNR = minSNR self.alpha = alpha self.beta = beta self.deltaM = deltaM self.zp_off = zp_off if survey_fields == None: self.survey_fields = self.DES_specific_zdist() else: self.survey_fields = survey_fields if cosmo == None: self.cosmo = FlatLambdaCDM(H0=70.0, Om0=0.3) else: self.cosmo = cosmo self.totz = self.get_zdist() print "Total number of sn to be simulated:", len(self.totz) start = time.time() if simlib_file: self.simlib_meta, self.simlib_obs_sets = self.read_simlib( simlib_file) else: self.simlib_obs_sets = simlib_obs_sets end = time.time() if bench: print "Read simlib file in ", end - start, "secs" self.c_pdf = c_pdf self.c_params = c_params self.x1_pdf = x1_pdf self.x1_params = x1_params start = time.time() self.generate_random_c_x1() end = time.time() if bench: print "c-x1 generated in ", end - start, "secs" self.generate_t0() #EJ: Note the model needs to be downloaded at this point - is there #anyway we can avoid this for users who are not online? dust = sncosmo.CCM89Dust() self.model = sncosmo.Model(source='salt2-extended',effects=[dust],effect_names=['mw'],\ effect_frames=['obs']) self.get_parameter_list() self.generate_lcs() start = time.time() self.fit_lcs() end = time.time() if bench: print "Fitting took", end - start, "secs" if setplot: for ii in range(len(self.fit_results)): plt.plot(self.simx0[ii],self.fit_results[ii]['parameters'][2],\ linestyle='None', marker="o") plt.savefig('x0.png') plt.close() for ii in range(len(self.fit_results)): plt.plot(self.simx1[ii],self.fit_results[ii]['parameters'][3],\ linestyle='None', marker="o") plt.savefig('x1.png') plt.close() for ii in range(len(self.fit_results)): plt.plot(self.simc[ii],self.fit_results[ii]['parameters'][4],\ linestyle='None', marker="o") plt.savefig('c.png') plt.close() #print self.fit_results[0].keys(), self.fit_results[0]['param_names'], self.fit_results[0]['parameters'] #print self.fit_results[0]['parameters'] #print self.fit_results[0]['covariance'], self.fit_results[0]['covariance'].shape #print self.totz[0],self.simt0[0], self.simx0[0], self.simx1[0], self.simc[0] #print self.lcs[0][0] #print self.lcs[0][0] #print self.fitted_model[0] sncosmo.plot_lc(self.lcs[0][0], model=self.fitted_model[0], errors=self.fit_results[0].errors) plt.savefig("lc.png")
def analyze(): pkl_file = open('ccm.pkl', 'r') amed = pickle.load(pkl_file) pkl_file.close() synlam = numpy.array([[3300.00, 3978.02], [3978.02, 4795.35], [4795.35, 5780.60], [5780.60, 6968.29], [6968.29, 8400.00]]) synname = ['U', 'B', 'V', 'R', 'I'] synbands = [] for name, lams in zip(synname, synlam): synbands.append(sncosmo.Bandpass(lams, [1., 1.], name='tophat' + name)) model_nodust = sncosmo.Model(source='hsiao') flux_nodust = model_nodust.bandflux(synbands, 0.) av = numpy.exp( numpy.arange(numpy.log(0.005), numpy.log(1.8) + 0.001, numpy.log(1.8 / 0.005) / 25)) rv = numpy.exp( numpy.arange(numpy.log(2.1), numpy.log(6.9) + 0.001, numpy.log(6.9 / 2.1) / 50)) avs = [] ebvs = [] rvs = [] AX = [] for a in av: for r in rv: dust = sncosmo.CCM89Dust() dust.set(ebv=a / r, r_v=r) model = sncosmo.Model(source='hsiao', effects=[dust], effect_names=['host'], effect_frames=['rest']) AX.append(-2.5 * numpy.log10(model.bandflux(synbands, 0.) / flux_nodust)) avs.append(a) ebvs.append(a / r) rvs.append(r) avs = numpy.array(avs) ebvs = numpy.array(ebvs) AX = numpy.array(AX) rvs = numpy.array(rvs) diff = AX - (amed[0][None,:]*avs[:,None]+ amed[1][None,:] * avs[:,None]**2 \ +amed[2][None,:]*ebvs[:,None]+ amed[3][None,:] * ebvs[:,None]**2 \ +amed[4][None,:] * (avs*ebvs)[:,None] \ +amed[5][None,:] * (avs**3)[:,None] \ +amed[6][None,:] * (ebvs**3)[:,None] \ +amed[7][None,:] * ((avs**2)*ebvs)[:,None] \ +amed[8][None,:] * (avs*(ebvs**2))[:,None] \ ) print numpy.max(numpy.abs(diff)) arg = numpy.argmax(numpy.abs(diff)) print avs[arg / 5], ebvs[arg / 5] print diff[arg / 5] print avs.max() wav = avs == 1.8 for i in xrange(5): plt.plot(rvs[wav], diff[wav, i], label=synname[i]) plt.ylabel(r'$\Delta A$') plt.xlabel(r'$R$') plt.legend() pp = PdfPages('output18/dfitz.pdf') plt.savefig(pp, format='pdf') pp.close() plt.close()
def save_FINAL_spectrum(self): if (not hasattr(self, 'final_mangled_spec')): print( "Mangled spectrum not available. First you need to mangle the spectrum calling self.mangle_spectrum()" ) if (not hasattr(self, 'FINALspec_path')): self.create_FINALspec_folder() flux = self.final_mangled_spec['flux'] fluxerr = self.final_mangled_spec['fluxerr'] sn_info = self.load_final_info() MWdust = sncosmo.CCM89Dust() Hostdust = sncosmo.CCM89Dust() r_v = 3.1 z = sn_info['z'].values[0] wls = self.ext_spec['wls'] wls_restframe = self.ext_spec['wls'] / (1 + z) # MW corrections in SN restframe MW_ebv = sn_info['EBV_MW'].values[0] MWdust.parameters = [MW_ebv, r_v] MW_extinction_perc = MWdust.propagate(wls_restframe, np.ones(len(flux))) # Host corrections in SN restframe Host_ebv = sn_info['EBV_host'].values[0] Hostdust.parameters = [Host_ebv, r_v] Host_extinction_perc = Hostdust.propagate(wls, np.ones(len(flux))) dist = 10**(mycosmo.distmod(z).value / 5. + 1) To_abs_lum = (dist / 10.)**2 self.final_spec_wls = wls_restframe self.HostCorr_spec_flux = To_abs_lum * flux self.HostCorr_spec_fluxerr = To_abs_lum * fluxerr self.HostNOTCorr_spec_flux = To_abs_lum * flux * Host_extinction_perc self.HostNOTCorr_spec_fluxerr = To_abs_lum * fluxerr * Host_extinction_perc self.as_observed_spec_flux = flux * Host_extinction_perc * MW_extinction_perc self.as_observed_spec_fluxerr = fluxerr * Host_extinction_perc * MW_extinction_perc spec_number = self.phot4mangling.spec_mjd.values[ 0] # self.phot4mangling.index.values[0] for path_fold in ['', '/HostNotCorr/', '/as_observed/']: path = self.FINALspec_path + path_fold if 'FL' in self.spec_file: fout = open( path + self.spec_file.replace('_spec_extended_FL.txt', '_FINAL_spec_FL.txt'), 'w') elif 'SMOOTH' in self.spec_file: fout = open( path + self.spec_file.replace('_spec_extended_SMOOTH.txt', '_FINAL_spec_SMOOTH.txt'), 'w') elif 'SNF' in self.spec_file: fout = open( path + self.spec_file.replace('_spec_extended_SNF.txt', '_FINAL_spec_SNF.txt'), 'w') else: fout = open(path + '/%.2f_FINAL_spec.txt' % spec_number, 'w') fout.write('#wls\tflux\tfluxerr\n') if path_fold == '': for w, f, ferr in zip(self.final_spec_wls, self.HostCorr_spec_flux, self.HostCorr_spec_fluxerr): fout.write('%E\t%E\t%E\n' % (w, f, ferr)) fout.close() elif path_fold == '/HostNotCorr/': for w, f, ferr in zip(self.final_spec_wls, self.HostNOTCorr_spec_flux, self.HostNOTCorr_spec_fluxerr): fout.write('%E\t%E\t%E\n' % (w, f, ferr)) fout.close() elif path_fold == '/as_observed/': for w, f, ferr in zip(self.final_spec_wls, self.as_observed_spec_flux, self.as_observed_spec_fluxerr): fout.write('%E\t%E\t%E\n' % (w, f, ferr)) fout.close()
def SNMC(df, name, window=652 / 365, scale=1e6): """ df should be the full data frame, with properties of all the targets it will be indexed with name for single target by 'our survey name' inside the function Using the SN rates with window of time to generate N SNe, [N/yr]*1.8 yr The window ~ final - first observation from read_slate """ avg_NIa = np.mean(df['NIa']) avg_eNIa = np.mean(df['e_NIa']) avg_Ncc = np.mean(df['Ncc']) avg_eNcc = np.mean(df['e_Ncc']) targ = df[df[' Our Survey Name '] == name] zS, mu = targ['zS'], targ['mu'] NIa = targ['NIa'] Ncc = targ['Ncc'] e_NIa = targ['e_NIa'] e_Ncc = targ['e_Ncc'] """ if pd.isna(NIa): # use the avg if rate not available, should only apply to SWELLSJ0841+3824 print('using avg SN rates for',name) NIa,e_NIa = avg_NIa,avg_eNIa """ # scale up rates to large enough numbers to get multiple models to plant NIa *= scale e_NIa *= scale Ncc *= scale e_Ncc *= scale # select a rate NIa = np.random.normal(NIa, e_NIa) if NIa < 0: NIa = 0 # use window to get number from rates if type(window) == dict: window = window['window'] window = window.days / 365 nIa = NIa * window # NIa [/yr], window ~ 652 days, n [expected number of Ia in window] ncc = Ncc * window # dusts """ for a review of dust http://www.astronomy.ohio-state.edu/~pogge/Ast871/Notes/Dust.pdf Observationally, RV ranges between 2 and 6, but most often one finds the interstellar extinction law assumed by people as adopting one of two “typical” values for RV: RV=3.1, typical of the Diffuse ISM. RV=5, typical of dense (molecular) clouds. """ mwdust = sncosmo.CCM89Dust() mwdust.set(ebv=0, r_v=3.1) # these are the defaults of CCM89Dust() hostdust = sncosmo.CCM89Dust() hostdust.set(ebv=0, r_v=3.1) import sfdmap dustmap = sfdmap.SFDMap( '/home/oconnorf/miniconda/lib/python3.7/site-packages/mwdust/dust_maps' ) # using Schlegel, Finkbeiner & Davis (1998) MW dust map to set EBV # get ra and dec of the SN ~ target ra = df['ra'] dec = df['dec'] print(ra, dec) #ra,dec=0.0,0.0 ebv = dustmap.ebv(ra, dec) mwdust.set(ebv=ebv) # Luminosity Functions: Goldstein 2019 Table 1, MB Vega, https://arxiv.org/pdf/1809.10147.pdf band, sys = 'bessellb', 'vega' MIa, sigmaIa = -19.23, 0.1 MIIp, sigmaIIp = -16.9, 1.12 MIIL, sigmaIIL = -17.46, 0.38 MIIn, sigmaIIn = -19.05, 0.5 MIbc, sigmaIbc = -17.51, 0.74 # Luminosity Functions: Converted to AB Mag System, http://www.astronomy.ohio-state.edu/~martini/usefuldata.html, Blanton et al. 2007 # B-Band m_AB - m_Vega ~ -0.09 band, sys = 'bessellb', 'ab' dm = -0.09 MIa += dm MIIp += dm MIIL += dm MIIn += dm MIbc += dm # fractions core collapse: Eldridge 2013, https://arxiv.org/abs/1301.1975 # Volume limited ~ 30 Mpc couple hundred transients in the sample fracIb, fracIc, fracIIp, fracIIL, fracIIn, fracIIb, fracIIpec = 0.09, 0.17, 0.55, 0.03, .024, 0.121, 0.01 # my combinations of eldridges subtypes to goldsteins fracIbc = fracIb + fracIc fracIIL = fracIIL + fracIIpec + fracIIb # if you want the check on CC fractions fracs = [fracIIp, fracIIL, fracIIn, fracIbc] # np.sum(fracs) """ Another possible source for fractions... Fractions core collapse: Smith 2010, https://arxiv.org/pdf/1006.3899.pdf Volume limited ~ 60 Mpc sample from LOSS """ # Stretch and Color Ia parameter distributions c = np.linspace(-0.3, 0.5, 1000) cdist = [f_asymmGauss(ci, -0.054, .043, 0.101) for ci in c] dc = (max(c) - min(c)) / len(c) cps = np.array(cdist) * dc tmp = (1 - np.sum(cps)) / len(c) cps = [i + tmp for i in cps] x1 = np.linspace(-3.0, 2.0, 1000) x1dist = [f_asymmGauss(xi, 0.973, 1.472, 0.222) for xi in x1] dx1 = (max(x1) - min(x1)) / len(x1) x1ps = np.array(x1dist) * dx1 tmp = (1 - np.sum(x1ps)) / len(x1) x1ps = [i + tmp for i in x1ps] # The models Iamodels = [] CCmodels = [] IIpmodels, IILmodels, IInmodels, Ibcmodels = [], [], [], [] # The mags if want to see those (without magnification) magIas = [] magIIps, magIILs, magIIns, magIbcs = [], [], [], [] cs, x1s = [], [] for i in range(int(nIa)): model = sncosmo.Model(source='salt2-extended', effects=[hostdust, mwdust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) # Ia magIa = np.random.normal(MIa, sigmaIa) magIas.append(magIa) # lensing magnification mabs = magIa - 2.5 * np.log10(mu) t0 = np.random.uniform(0, int(window * 365)) # stretch list_of_candidates, number_of_items_to_pick, probability_distribution = x1, 1, x1ps x1draw = choice(list_of_candidates, number_of_items_to_pick, p=probability_distribution) x1s.append(x1draw) # color list_of_candidates, number_of_items_to_pick, probability_distribution = c, 1, cps cdraw = choice(list_of_candidates, number_of_items_to_pick, p=probability_distribution) cs.append(cdraw) # set the values model.set(z=zS, t0=t0, c=cdraw, x1=x1draw) model.set_source_peakabsmag(mabs, band, sys) Iamodels.append(model) for i in range(int(fracIIp * ncc)): model = sncosmo.Model(source='s11-2005lc', effects=[hostdust, mwdust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) # IIp magIIp = np.random.normal(MIIp, sigmaIIp) magIIps.append(magIIp) mabs = magIIp - 2.5 * np.log10(mu) t0 = np.random.uniform(0, int(window * 365)) model.set(z=zS, t0=t0) model.set_source_peakabsmag(mabs, band, sys) IIpmodels.append(model) CCmodels.append(IIpmodels) for i in range(int(fracIIL * ncc)): model = sncosmo.Model(source='nugent-sn2l', effects=[hostdust, mwdust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) # IIL magIIL = np.random.normal(MIIL, sigmaIIL) magIILs.append(magIIL) mabs = magIIL - 2.5 * np.log10(mu) t0 = np.random.uniform(0, int(window * 365)) model.set(z=zS, t0=t0) model.set_source_peakabsmag(mabs, band, sys) IILmodels.append(model) CCmodels.append(IILmodels) for i in range(int(fracIIn * ncc)): model = sncosmo.Model(source='nugent-sn2n', effects=[hostdust, mwdust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) # IIn magIIn = np.random.normal(MIIn, sigmaIIn) magIIns.append(magIIn) mabs = magIIn - 2.5 * np.log10(mu) t0 = np.random.uniform(0, int(window * 365)) model.set(z=zS, t0=t0) model.set_source_peakabsmag(mabs, band, sys) IInmodels.append(model) CCmodels.append(IInmodels) for i in range(int(fracIbc * ncc)): model = sncosmo.Model(source='nugent-sn1bc', effects=[hostdust, mwdust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) # Ibc magIbc = np.random.normal(MIbc, sigmaIbc) magIbcs.append(magIbc) mabs = magIbc - 2.5 * np.log10(mu) t0 = np.random.uniform(0, int(window * 365)) model.set(z=zS, t0=t0) model.set_source_peakabsmag(mabs, band, sys) Ibcmodels.append(model) CCmodels.append(Ibcmodels) return [Iamodels, CCmodels]
minsnr=minsnr) reschar = ResChar.fromSNCosmoRes(resfit) return snid, reschar snana_eg = SNANASims.fromSNANAfileroot( snanafileroot='LSST_Ia', location='/Users/rbiswas/data/LSST/SNANA_data/MINION_1016_10YR_DDF_v2/', coerce_inds2int=False) if __name__ == '__main__': snana_eg = SNANASims.fromSNANAfileroot( snanafileroot='LSST_Ia', location='/Users/rbiswas/data/LSST/SNANA_data/MINION_1016_10YR_DDF_v2/', coerce_inds2int=False) dust = sncosmo.CCM89Dust() model = sncosmo.Model(source='salt2-extended', effects=[dust, dust], effect_names=['host', 'mw'], effect_frames=['rest', 'obs']) for i in range(3): try: snid, r = inferParams(snana_eg, model, sncosmo.fit_lc, i, minsnr=3.) with open('results.dat', 'w') as fh: # Should Not be a text file when improved! write_str = snid write_str += ','.join(map(str, r.parameters))
def dofit(datfile='nebra_bestphot.dat', z=2.00, dz=0.02, t0=57575., dt0=20.0, x1=None, c=None, model='Ia', noUV=True, debug=False): # TODO : read in the redshift, etc from the header. from .colorcolorfig import SubClassDict_SNANA # read in the obs data sn = ascii.read(datfile, format='commented_header', header_start=-1, data_start=0) if model == 'Ia': # define SALT2 models and set initial guesses for z and t0 if noUV: salt2ex = sncosmo.Model(source='salt2') else: salt2ex = sncosmo.Model(source='salt2-extended') salt2ex.source.set_peakmag(0., 'bessellb', 'ab') x0_AB0 = salt2ex.get('x0') salt2ex.set(z=z, t0=t0, x1=0.1, c=-0.2) # salt2ex.set( z=1.33, t0=56814.6, hostebv=0.05, hostr_v=3.1 ) # Do a bounded fit : # salt2res, salt2fit = sncosmo.fit_lc( sn, salt2, ['z','t0','x0','x1','c'], bounds={'z':(1.28,1.37),'t0':(56804,56824)} ) varlist = varlist = ['z', 't0', 'x0'] bounds = {'z': (z - dz, z + dz), 't0': (t0 - dt0, t0 + dt0)} if x1 is not None: salt2ex.set(x1=x1) bounds['x1'] = (x1 - 1e-6, x1 + 1e-6) varlist.append('x1') else: bounds['x1'] = (-5, 5) varlist.append('x1') if c is not None: salt2ex.set(c=c) else: bounds['c'] = (-0.5, 3.0) varlist.append('c') res, fit = sncosmo.fit_lc(sn, salt2ex, varlist, bounds) x0 = fit.get('x0') z = fit.get('z') mB = -2.5 * np.log10(x0 / x0_AB0) distmod = mB - MBmodel deltamuLCDM = distmod - dm(z) print("mB = %.2f" % mB) print("dist.mod. = %.2f" % distmod) print("Delta.mu_LCDM = %.2f" % deltamuLCDM) chi2 = res.chisq ndof = res.ndof pval = chisqprob(chi2, ndof) if ndof > 0: print("chi2/dof= %.3f" % (chi2 / float(ndof))) print("p-value = %.3f" % pval) else: print("chi2/dof= %.3f/%i" % (chi2, ndof)) print("p-value = %.3f" % pval) print("z = %.3f" % fit.get('z')) print("t0 = %.3f" % fit.get('t0')) print("x0 = %.3e" % fit.get('x0')) print("x1 = %.3f" % fit.get('x1')) print("c = %.3f" % fit.get('c')) elif model.lower() in ['cc', 'ib', 'ic', 'ii', 'ibc', 'iip', 'iin']: # remove the blue filters from the sn data bandlist = sn['filter'].data igood = np.array([band.lower().startswith('f1') for band in bandlist]) sn = sn.copy()[igood] # define a host-galaxy dust model dust = sncosmo.CCM89Dust() version = '1.0' if model.lower() == 'cc': classlist = ['Ib', 'Ic', 'IIP', 'IIn'] elif model.lower() == 'ii': classlist = ['IIP', 'IIn'] elif model.lower() == 'ibc': classlist = ['Ibc'] else: classlist = [model] # find the best-fit from each CC sub-class chi2list, reslist, fitlist = [], [], [] for snclass in classlist: for modname in SubClassDict_SNANA[snclass.lower()]: Av = 0.2 modkey = (sncosmo.Source, modname, version) if modkey not in sncosmo.registry._loaders: continue ccmodel = sncosmo.Model(source=modname, effects=[dust], effect_names=['host'], effect_frames=['rest']) ccmodel.set(z=z, t0=t0, hostr_v=3.1, hostebv=Av / 3.1) # Do a bounded fit : res, fit = sncosmo.fit_lc(sn, ccmodel, ['z', 't0', 'amplitude', 'hostebv'], debug=debug, bounds={ 'z': (z - dz, z + dz), 't0': (t0 - dt0, t0 + dt0), 'hostebv': (0.0, 1.0) }) chi2 = res.chisq ndof = res.ndof pval = chisqprob(chi2, ndof) print("%s chi2/dof= %.3f p=%.3f" % (modname, chi2 / float(ndof), pval)) chi2list.append(chi2 / float(ndof)) reslist.append(res) fitlist.append(fit) ichi2min = np.argmin(chi2list) res, fit = reslist[ichi2min], fitlist[ichi2min] else: # 'nugent-sn91bg' # remove the blue filters from the sn data bandlist = sn['filter'].data igood = np.array([band.startswith('f1') for band in bandlist]) sn = sn.copy()[igood] # define a host-galaxy dust model dust = sncosmo.CCM89Dust() version = '1.0' Av = 0.2 altmodel = sncosmo.Model(source=model, effects=[dust], effect_names=['host'], effect_frames=['rest']) altmodel.set(z=z, t0=t0, hostr_v=3.1, hostebv=Av / 3.1) # Do a bounded fit : res, fit = sncosmo.fit_lc(sn, altmodel, ['z', 't0', 'amplitude', 'hostebv'], debug=debug, bounds={ 'z': (z - dz, z + dz), 't0': (t0 - dt0, t0 + dt0), 'hostebv': (0.0, 1.0) }) chi2 = res.chisq ndof = res.ndof pval = chisqprob(chi2, ndof) print("%s chi2/dof= %.3f p=%.3f" % (model, chi2 / float(ndof), pval)) return (sn, fit, res)
def setup_class(self): self.model = sncosmo.Model(source=flatsource(), effects=[sncosmo.CCM89Dust()], effect_frames=['obs'], effect_names=['mw'])
def light_curves_salt2(z: float, filters: list, peak: float = None, days: Union[tuple, list, np.ndarray] = (0, 85), show: bool = True, rise_time: float = None, ebv_mw: float = 0, ebv_host: float = 0., x1: float = None, output_path: str = None, output_title: str = None, day_markers: list = None, fil_peak='bessellb', r_v: float = 2.3): """ Produces light curves, in the provided filters, for a Type Ia supernova using the SALT2 models as implemented in sncosmo. :param z: Redshift of source. :param filters: Filters to obtain light curves in. Must be in the sncosmo Registry. :param peak: Peak (ie lowest) absolute magnitude, in the filter given by fil_peak, to calibrate curves to. :param days: Either an array of times (in days) to calculate the light curves over, or a tuple describing the range of days, ie (first_day, last_day) :param show: Show plot onscreen? :param rise_time: Rest-frame time, from beginning of SN to peak magnitude, in days. :param ebv_mw: Reddening parameter E(B-V), using S&F11 law, for the Milky Way along the SN's line-of-sight. :param ebv_host: Reddening parameter E(B-V), using S&F11 law, for the host galaxy. :param x1: SALT2 light curve stretch parameter. See SALT2 documentation for further information. :param output_path: Path to which to save output. If None, does not save. :param output_title: Title to give output plot and table. :param day_markers: List of times (in days) to mark on plot, eg observation dates. :param fil_peak: Filter in which to set the peak absolute magnitude; usually reported in B or V. :return: mag_table, model mag_table: an astropy.table.Table with the times, in days, and the magnitudes in each filter. model: the entire sncosmo model instance. """ if output_path is not None and output_path[-1] != '/': output_path += '/' u.mkdir_check(output_path) # Find time at which model begins and time of peak. t_first, t_peak = find_model_times(source='salt2-extended', z=z, fil=fil_peak, show=False) # If both are None, the model is invalid over these wavelengths if t_first is t_peak is None: return None, None # Set up model in sncosmo. dust_mw = sncosmo.F99Dust() dust_host = sncosmo.CCM89Dust() model = sncosmo.Model(source='salt2-extended', effects=[dust_mw, dust_host], effect_names=['mw', 'host'], effect_frames=['obs', 'rest']) model.set(x1=x1) # If a rise time is not given, allow the model to determine this itself. if rise_time is None: if t_peak <= 0: model.set(z=z, t0=-t_first, mwebv=ebv_mw, hostebv=ebv_host, hostr_v=r_v) else: model.set(z=z, t0=0, mwebv=ebv_mw, hostebv=ebv_host, hostr_v=r_v) elif rise_time == -1: model.set(z=z, t0=t_first, mwebv=ebv_mw, hostebv=ebv_host, hostr_v=r_v) else: # Correct rise time to observer frame rise_time_obs = rise_time * (1 + z) model.set(z=z, t0=rise_time_obs - t_peak, mwebv=ebv_mw, hostebv=ebv_host, hostr_v=r_v) print(model) # Set peak absolute magnitude of model. if peak is not None: model.set_source_peakabsmag(peak, fil_peak, 'ab') if type(days) is tuple: # Set up array of times. days = np.arange(days[0], days[1] + 1, 0.1) mags_filters = table.Table() mags_filters['days'] = days maxes = [] t_peaks = [] peaks = [] for f in filters: # Get light curve. mags = model.bandmag(f, 'ab', days) # If the light curve is mostly flat, the model has probably broken down. if np.sum(mags == mags[0]) < 0.9 * len(mags): # If this is False, the entire light curve must be nan, and we'll get nothing useful out of it. if not np.isnan(np.nanmax(mags)): # Collect peak (lowest) magnitudes, peak times, and maximum (faintest) magnitudes for each filter. maxes.append(np.nanmax(mags[mags != np.inf])) peaks.append(np.nanmin(mags[mags != np.inf])) t_peaks.append(days[np.nanargmin(mags)]) # Write light curve to table. mags_filters[f] = mags if output_path is not None or show: # Plot curve. plt.plot(days, mags, label=f) # If we have no maxima, the model has broken down. if len(maxes) > 0: # Collect this for plotting purposes. max_mag = np.nanmax(maxes) min_mag = np.nanmin(peaks) else: return None, None # If an output_path directory is not given and 'show' is not True, there's no point doing the plot. if output_path is not None or show: for i, t_peak in enumerate(t_peaks): # Plot blue lines marking the peak of each filter light curve. plt.plot([t_peak, t_peak], [max_mag + 1, min_mag - 1], c='blue') if day_markers is not None: for other_day in day_markers: # Plot red lines marking the observation dates. plt.plot([other_day, other_day], [max_mag + 1, min_mag - 1], c='red') plt.xlabel('Time (days)') plt.ylabel('Magnitude') plt.ylim(max_mag + 1, min_mag - 1) plt.legend() if output_path is not None: # Save figure. plt.savefig(output_path + output_title + '.png') # Save table to csv. mags_filters.write(output_path + output_title + '.csv', format='ascii.csv') if show: # Show figure onscreen. plt.show() plt.close() return mags_filters, model