Tutorial: Generating Macronova lightcurves based on ztf_sim output

This notebook shows how to load the output for Eric's survey simulator ztf_sim and generate Macronova lightcurves for it using an SED from Rosswog et al. (2016). (Check out the other notebooks for examples how to simulate other transients.)

Note: You need to download Eric's newest sample output here. The link was also included in Eric's email, so you will likely only need to change the path below.

Furthermore you'll require the dust map from Schlegel, Finkbeiner & Davis (1998) for full functionality. It can be found here.


In [1]:
import os
home_dir = os.environ.get('HOME')

# Please enter the filename of the ztf_sim output file you would like to use. The example first determines
# your home directory and then uses a relative path (useful if working on several machines with different usernames)
survey_file = os.path.join(home_dir, 'data/ZTF/test_schedule_v6.db')

# Please enter the path to where you have placed the Schlegel, Finkbeiner & Davis (1998) dust map files
# You can also set the environment variable SFD_DIR to this path (in that case the variable below should be None)
sfd98_dir = os.path.join(home_dir, 'data/sfd98')

In [2]:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

import simsurvey
import sncosmo
from astropy.cosmology import Planck15

import simsurvey_tools as sst

In [3]:
# Load the ZTF CCD corners and filters
ccds = sst.load_ztf_ccds()
sst.load_ztf_filters()

In [4]:
# Load simulated survey from file (download from ftp://ftp.astro.caltech.edu/users/ebellm/one_year_sim_incomplete.db)
# Currently DES filters are used as proxies for ZTF filters
plan = simsurvey.SurveyPlan(load_opsim=survey_file, band_dict={'g': 'ztfg', 'r': 'ztfr', 'i': 'desi'}, ccds=ccds)

mjd_range = (plan.cadence['time'].min() - 30, plan.cadence['time'].max() + 30)

In [5]:
# To review the pointing schedule, you can use this table
plan.pointings


Out[5]:
<Table length=128477>
timebandzpskynoiseRADecfieldcomment
float64str4int64float64float64float64int64unicode13
57433.16732874228ztfr301235.45825479312997.27032-24.25258all_sky
57433.167791705244ztfr301032.415561403389693.69218000000001-17.05307all_sky
57433.16825466821ztfr30831.160655475382996.84022-9.85358all_sky
57433.16874926458ztfr30768.01432553431189.369684.55459all_sky
57433.16922019285ztfr30754.615141346931185.5846311.75511all_sky
57433.169683155815ztfr30845.739329548502882.35884.55458all_sky
57433.170146118784ztfr30754.615141346931185.5846311.75511all_sky
57433.170609081746ztfr30717.256118437486388.3730918.95562nightly_plane
57433.171118378836ztfr30666.061663741040591.2468626.15612all_sky
57433.171581341805ztfr30666.061663741040591.2468626.15612nightly_plane
........................
57706.412179100786ztfg301488.667313911661377.1428669.35837all_sky
57706.412662422554ztfg301553.241249284032686.6666762.15812all_sky
57706.413125385516ztfg301444.029966600034982.0524454.95780all_sky
57706.41358834848ztfg301416.803845784154281.8535447.75744all_sky
57706.41410365161ztfg301432.952975692837580.2314140.55703nightly_plane
57706.414577398355ztfg301467.57312783621672.2538547.75743nightly_plane
57706.41505362554ztfg301463.543339976470971.1276854.95779nightly_plane
57706.41552502806ztfg301550.010568972826873.3333362.15811all_sky
57706.41602904431ztfg301578.244011156435459.9999999999999976.55856all_sky
57706.41649200727ztfg301677.488192211204359.9999999999999969.35836all_sky

Transient Model

In this example the transient model is create from an ASCII file. Alternatively you could use the built-in SN models of sncosmo or the Blackbody model provided in simsurvey.


In [6]:
# Load phase, wavelengths and flux from file
phase, wave, flux = sncosmo.read_griddata_ascii('data/macronova_sed_wind20.dat')

# Create a time series source
source = sncosmo.TimeSeriesSource(phase, wave, flux)

# Create the model that combines SED and propagation effects
dust = sncosmo.CCM89Dust()
model = sncosmo.Model(source=source,
                      effects=[dust],
                      effect_names=['host'],
                      effect_frames=['rest'])

Transient distribution

You need to define a function that draws the model parameters (except z and t0) from a random distribution. In this case only host exinction is random but in addition the amplitude of each MN must be scaled for its luminosity distance.


In [7]:
def random_parameters(redshifts, model,
                      r_v=2., ebv_rate=0.11,
                      **kwargs):
    cosmo = Planck15
    
    # Amplitude
    amp = []
    for z in redshifts:
        d_l = cosmo.luminosity_distance(z).value * 1e5
        amp.append(d_l**-2)

    return {
        'amplitude': np.array(amp),
        'hostr_v': r_v * np.ones(len(redshifts)),
        'hostebv': np.random.exponential(ebv_rate, len(redshifts))
    }

TransientGenerator

The transient generator combines model and ditribution, and randomly draws all parameters needed to simulate the lightcurves.

(Note that here we also set the volumetric rate as funtion of z. For macronovae, a good guess would be $5\cdot 10^{-7}~\textrm{Mpc}^{-3}~\textrm{yr}^{-1}$ but this would only results in a couple of observed macronovae. For this example we'll use a 100 times larger rate.)


In [8]:
transientprop = dict(lcmodel=model,
                     lcsimul_func=random_parameters)

tr = simsurvey.get_transient_generator((0.0, 0.05),
                                       ratefunc=lambda z: 5e-5,
                                       dec_range=(-30,90),
                                       mjd_range=(mjd_range[0],
                                                  mjd_range[1]),
                                       transientprop=transientprop,
                                       sfd98_dir=sfd98_dir)

SimulSurvey

Lastly, all parts are combined in a SimulSurvey object that will generate the lightcurves. (This may take about a minute or two.)


In [9]:
survey = simsurvey.SimulSurvey(generator=tr, plan=plan)
    
lcs = survey.get_lightcurves(
    #progress_bar=True, notebook=True # If you get an error because of the progress_bar, delete this line.
)


/home/ufeindt/.local/lib/python2.7/site-packages/simsurvey-0.4.0-py2.7.egg/simsurvey/simulsurvey.py:1355: RuntimeWarning: invalid value encountered in log10

In [10]:
len(lcs.lcs)


Out[10]:
83

In [11]:
lcs[0]


Out[11]:
<Table length=90>
timebandfluxfluxerrzpzpsysfieldccdcomment
float64str4float64float64int64str2int64int64unicode7
57615.38283656832ztfr99.56272510084517749.350506924393530ab55015all_sky
57615.38378356504ztfr495.8434591898682749.350506924393530ab55015all_sky
57615.3842688382ztfr241.71562773918544766.64578578558430ab55112all_sky
57615.41324923264ztfr-412.5808522111392669.68685490395530ab55112all_sky
57615.414192677636ztfr2189.2933121681253669.68685490395530ab55112all_sky
57615.41561484692ztfr-980.3066052218165668.865038483746330ab55015all_sky
57615.41948288944ztfg-937.4525519787344537.073618314620730ab55112all_sky
57616.37801500868ztfr-1042.1010786953661891.158334194573430ab55015all_sky
57616.378477971644ztfr1347.9058069156897891.158334194573430ab55015all_sky
57616.37896053899ztfr-594.6875334453994891.151055874841430ab55112all_sky
...........................
57641.37861567019ztfr-45.82509865637937456.2612269866149330ab55112all_sky
57641.38050498855ztfr-572.8123761510442459.4467022932334430ab55015all_sky
57643.291735624305desi361.9428443229057679.88940816507730ab55015i_band
57643.2942117337ztfr-342.978387213293687.736695502866230ab55015all_sky
57643.37435340489ztfr-623.1466369256693547.418861428622230ab55015all_sky
57643.374816367854ztfr232.92978251595395547.418860907495930ab55015all_sky
57643.38018475206ztfr166.5506097313659466.6709568152054430ab55112all_sky
57643.38064771503ztfr-25.477984666053494466.67095620716630ab55112all_sky
57643.41529714246ztfg-492.50500635963624448.680201579167430ab55015all_sky
57643.41578301304ztfg-210.8886557296866446.184627254695430ab55112all_sky

In [ ]:

Analysing the output

The output of get_lightcurves() is a LightcurveCollection object. Lightcurves are automatically filter, so only those that would be detected in the survey are kept.

You can save a the lightcurves in a pickle file and load them again later without rerunning the simulation.


In [12]:
lcs.save('lcs_tutorial_mne.pkl')

In [13]:
lcs = simsurvey.LightcurveCollection(load='lcs_tutorial_mne.pkl')

You can inspect the lightcurves manually. This example should return the lightcurve with the most points with S/N > 5.


In [14]:
_ = sncosmo.plot_lc(lcs[0])


The two figures below show how early the MNe are detected and at what redshifts. The simulation input parameters of transients that were not detected are also kept, so can check completeness.


In [15]:
plt.hist(lcs.stats['p_det'], lw=2, histtype='step', range=(0,10), bins=20)
plt.xlabel('Detection phase (observer-frame)', fontsize='x-large')
_ = plt.ylabel(r'$N_{MNe}$', fontsize='x-large')



In [16]:
plt.hist(lcs.meta_full['z'], lw=1, histtype='step', range=(0,0.05), bins=20, label='all')
plt.hist(lcs.meta['z'], lw=2, histtype='step', range=(0,0.05), bins=20, label='detected')
plt.xlabel('Redshift', fontsize='x-large')
plt.ylabel(r'$N_{MNe}$', fontsize='x-large')
plt.xlim((0, 0.05))
plt.legend()


Out[16]:
<matplotlib.legend.Legend at 0x7f055c7efb10>

In [ ]: