-
Notifications
You must be signed in to change notification settings - Fork 57
Add kilonova event source type #445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
bdd12a4
8ad3f80
9d6d3a7
e648e85
04d8905
5e69347
da6ef0b
e5c69e7
33c33b2
9231372
b8f2c6d
6e4e9ad
a5249f7
e99095f
db6a796
eb0d744
bdaecf8
c21b3bc
a4af741
89b3e43
f16eebb
f3da26b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| import numpy as np | ||
|
|
||
| if not hasattr(np, "trapezoid"): | ||
| np.trapezoid = np.trapz | ||
|
|
||
| from astropy import cosmology | ||
| from redback.transient_models import kilonova_models | ||
|
|
||
|
|
||
| class Kilonova: | ||
| """Class for initializing a kilonova light curve model. | ||
|
|
||
| If modeldir is provided, external kilonova model files are expected. | ||
| This option is currently not supported. If modeldir is not provided, | ||
| the model is retrieved from Redback's built-in kilonova models. By | ||
| default, the MOSFiT-based kilonova model is used. Information about | ||
| Redback can be found at | ||
| https://redback.readthedocs.io/en/latest/. | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| redshift, | ||
| model_name="mosfit_kilonova", | ||
| ejecta_mass=None, | ||
| ejecta_velocity=None, | ||
| opacity=None, | ||
| temperature_floor=None, | ||
| kappa_gamma=10, | ||
| mag_zpsys="AB", | ||
| cosmo=cosmology.FlatLambdaCDM(H0=70, Om0=0.3), | ||
| modeldir=None, | ||
| **kwargs, | ||
| ): | ||
| """ | ||
| :param redshift: The redshift of the kilonova source. | ||
| :type redshift: float | ||
| :param model_name: The kilonova light curve model to be used. If not provided, | ||
| the default model is the MOSFiT-based kilonova model. | ||
| :type model_name: str | ||
| :param ejecta_mass: Ejecta masses for the kilonova components in [M_sol]. | ||
| :type ejecta_mass: array-like or None | ||
| :param ejecta_velocity: Ejecta velocities for the kilonova components in units of | ||
| the speed of light [c]. | ||
| :type ejecta_velocity: array-like or None | ||
| :param opacity: Opacities for the kilonova components in [cm^2 g^-1]. | ||
| :type opacity: array-like or None | ||
| :param temperature_floor: Temperature floors for the kilonova components in [K]. | ||
| :type temperature_floor: array-like or None | ||
| :param kappa_gamma: Gamma-ray opacity in [cm^2 g^-1]. | ||
| :type kappa_gamma: float | ||
| :param mag_zpsys: Optional, AB or Vega (AB default). | ||
| :type mag_zpsys: str | ||
| :param cosmo: Cosmology for luminosity distance calculation. | ||
| :type cosmo: `~astropy.cosmology` | ||
| :param modeldir: Directory including files for external kilonova models. | ||
| :type modeldir: str or None | ||
| :param kwargs: Additional keyword arguments passed to the Redback kilonova model. | ||
| :type kwargs: dict | ||
| """ | ||
|
|
||
| if modeldir is not None: | ||
| # external kilonova model | ||
| raise NotImplementedError( | ||
| "External kilonova model files are not supported yet." | ||
| ) | ||
| else: | ||
| # use Redback built-in kilonova model, e.g. mosfit_kilonova | ||
| if not hasattr(kilonova_models, model_name): | ||
| raise ValueError( | ||
| f"{model_name} is not available in " | ||
| "redback.transient_models.kilonova_models." | ||
| ) | ||
| else: | ||
| self._model = getattr(kilonova_models, model_name) | ||
|
|
||
| self._model_name = model_name | ||
| self._redshift = redshift | ||
| self._mag_zpsys = mag_zpsys | ||
| self._cosmo = cosmo | ||
| self._kwargs = kwargs | ||
|
|
||
| parameter_groups = { | ||
| "ejecta_mass": ejecta_mass, | ||
| "ejecta_velocity": ejecta_velocity, | ||
| "opacity": opacity, | ||
| "temperature_floor": temperature_floor, | ||
| } | ||
|
|
||
| for name, values in parameter_groups.items(): | ||
| if values is None: | ||
| raise ValueError(f"{name} must be provided.") | ||
| if len(values) != 3: | ||
| # The MOSFiT kilonova model uses three ejecta components. | ||
| # Each of these parameters must therefore contain three values. | ||
| raise ValueError(f"{name} must have three components.") | ||
|
|
||
| self._model_parameters = { | ||
| "mej_1": ejecta_mass[0], | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. perhaps have all these parameters as inputs to this class instead of re-naming them |
||
| "mej_2": ejecta_mass[1], | ||
| "mej_3": ejecta_mass[2], | ||
| "vej_1": ejecta_velocity[0], | ||
| "vej_2": ejecta_velocity[1], | ||
| "vej_3": ejecta_velocity[2], | ||
| "kappa_1": opacity[0], | ||
| "kappa_2": opacity[1], | ||
| "kappa_3": opacity[2], | ||
| "temperature_floor_1": temperature_floor[0], | ||
| "temperature_floor_2": temperature_floor[1], | ||
| "temperature_floor_3": temperature_floor[2], | ||
| "kappa_gamma": kappa_gamma, | ||
| } | ||
|
|
||
| def get_apparent_magnitude(self, time, band, zpsys="AB"): | ||
| """Function to return apparent magnitude of a kilonova for a given band | ||
| and time. | ||
|
|
||
| :param time: The observer-frame time array to evaluate the model | ||
| (in days) | ||
| :type time: array-like | ||
| :param band: The band to evaluate the model over. | ||
| :type band: str or list | ||
| :param zpsys: Optional, AB or Vega (AB default) | ||
| :type zpsys: str | ||
| :return: magnitude of source | ||
| """ | ||
|
|
||
| return self._model( | ||
| time=time, | ||
| redshift=self._redshift, | ||
| bands=band, | ||
| output_format="magnitude", | ||
| cosmology=self._cosmo, | ||
| **self._model_parameters, | ||
| **self._kwargs, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| import warnings | ||
| from slsim.Sources.SourceTypes.source_base import SourceBase | ||
| from slsim.Sources.Events.BNSMerger.kilonova import Kilonova | ||
| from slsim.ImageSimulation.image_quality_lenstronomy import ( | ||
| get_all_supported_bands, | ||
| get_sncosmo_filtername, | ||
| ) | ||
|
|
||
|
|
||
| class KilonovaEvent(SourceBase): | ||
| """A class to manage a BNS merger.""" | ||
|
|
||
| def __init__( | ||
| self, | ||
| lightcurve_time, | ||
| variability_model, | ||
| model_name="mosfit_kilonova", | ||
| mag_zpsys="AB", | ||
| modeldir=None, | ||
| kwargs_variability=None, | ||
| kwargs_kilonova=None, | ||
| cosmo=None, | ||
| **kwargs, | ||
| ): | ||
| """ | ||
| :param lightcurve_time: Observation time array for the light curve in [days]. | ||
| :type lightcurve_time: array-like | ||
| :param variability_model: Keyword for the variability model to be used. This is an | ||
| input for the Variability class. | ||
| :type variability_model: str | ||
| :param model_name: Kilonova light curve model to be used. If not provided, the | ||
| default model is the MOSFiT-based kilonova model. | ||
| :type model_name: str | ||
| :param mag_zpsys: Optional, AB or Vega (AB default). | ||
| :type mag_zpsys: str | ||
| :param modeldir: Directory including files for external kilonova models. This | ||
| option is currently not supported. | ||
| :type modeldir: str or None | ||
| :param kwargs_variability: List containing the variability keyword and the | ||
| bands for which the light curve should be generated. | ||
| :type kwargs_variability: list or None | ||
| :param kwargs_kilonova: Keyword arguments passed to the Kilonova class. This may | ||
| include ejecta_mass in [solar masses], ejecta_velocity in [c], opacity in | ||
| [cm^2 g^-1], temperature_floor in [K], and kappa_gamma in [cm^2 g^-1]. | ||
| :type kwargs_kilonova: dict or None | ||
| :param cosmo: Astropy cosmology instance. | ||
| :type cosmo: `~astropy.cosmology` | ||
| :param kwargs: Keyword arguments passed to the SourceBase class. This may contain | ||
| source properties such as redshift and offsets from the host galaxy in [arcsec]. | ||
| :type kwargs: dict | ||
| """ | ||
| super().__init__( | ||
| extended_source=False, | ||
| point_source=True, | ||
| cosmo=cosmo, | ||
| variability_model=variability_model, | ||
| **kwargs, | ||
| ) | ||
| self.name = "BNS" | ||
| self._variability_computed = False | ||
| self._kwargs_variability = kwargs_variability | ||
| self._lightcurve_time = lightcurve_time | ||
|
|
||
| self._model_name = model_name | ||
| self._mag_zpsys = mag_zpsys | ||
| self._modeldir = modeldir | ||
|
|
||
| if kwargs_kilonova is None: | ||
| self._kwargs_kilonova = {} | ||
| else: | ||
| self._kwargs_kilonova = kwargs_kilonova | ||
|
|
||
| @property | ||
| def light_curve(self): | ||
| """Provides lightcurves of a bns merger in each band.""" | ||
| if self._kwargs_variability is not None: | ||
| kwargs_variab_extracted = {} | ||
| if self._cosmo is None: | ||
| raise ValueError( | ||
| "Cosmology cannot be None for BNSMerger class. Please" | ||
| "provide a suitable astropy cosmology." | ||
| ) | ||
| else: | ||
| # Initialize BNS/Kilonova light curve model | ||
| lightcurve_class = Kilonova( | ||
| redshift=self._z, | ||
| model_name=self._model_name, | ||
| mag_zpsys=self._mag_zpsys, | ||
| cosmo=self._cosmo, | ||
| modeldir=self._modeldir, | ||
| **self._kwargs_kilonova, | ||
| ) | ||
| self._lightcurve_class = lightcurve_class | ||
|
|
||
| supported_bands = get_all_supported_bands() | ||
| provided_bands = set(supported_bands) & set(self._kwargs_variability) | ||
|
|
||
| for element in provided_bands: | ||
| name = "ps_mag_" + element | ||
| times = self._lightcurve_time | ||
|
|
||
| # Convert SLSim short band labels, such as "i" and "r", to registered | ||
| # filter names required by Redback, such as "lssti" and "lsstr". | ||
| # We reuse the existing SLSim/SNcosmo filter-name helper for this mapping. | ||
| provided_band = get_sncosmo_filtername(element) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need SNCosmo band names? don't understand how this is required |
||
|
|
||
| try: | ||
| magnitudes = lightcurve_class.get_apparent_magnitude( | ||
| time=times, | ||
| band=provided_band, | ||
| zpsys=self._mag_zpsys, | ||
| ) | ||
| except Exception as e: | ||
| warnings.warn( | ||
| f"Skipping band '{provided_band}': Failed to generate lightcurve. " | ||
| f"(Error: {e})", | ||
| UserWarning, | ||
| ) | ||
| continue | ||
|
|
||
| if name not in self.source_dict: | ||
| self.source_dict[name] = float(min(magnitudes)) | ||
|
|
||
| kwargs_variab_extracted[element] = { | ||
| "MJD": times, | ||
| name: magnitudes, | ||
| } | ||
| else: | ||
| kwargs_variab_extracted = {} | ||
|
|
||
| self._variability_computed = True | ||
| return kwargs_variab_extracted | ||
|
|
||
| def point_source_magnitude(self, band, image_observation_times=None): | ||
| """Get the magnitude of the BNS/kilonova point source in a specific | ||
| band. | ||
|
|
||
| :param band: Imaging band. | ||
| :type band: str | ||
| :param image_observation_times: Image observation times in | ||
| [days]. If None, takes the peak magnitude. | ||
| :type image_observation_times: array-like or None | ||
| :return: Magnitude of the point source in the specified band. | ||
| :rtype: float or array-like | ||
| """ | ||
|
|
||
| if not self._variability_computed: | ||
| self._kwargs_variability_model = self.light_curve | ||
|
|
||
| return super().point_source_magnitude( | ||
| band=band, image_observation_times=image_observation_times | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,3 +7,4 @@ pyhalo<=1.4.6 | |
| mcfit | ||
| sncosmo | ||
| stpsf | ||
| redback>=1.17.0 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this test here? Why are there three values? Can you explain what you test here in the code?