diff --git a/pycbc/distributions/constraints.py b/pycbc/distributions/constraints.py index 6c830c8bdb1..151ded3529c 100644 --- a/pycbc/distributions/constraints.py +++ b/pycbc/distributions/constraints.py @@ -16,9 +16,13 @@ This modules provides classes for evaluating multi-dimensional constraints. """ +import scipy.spatial +import numpy +import h5py from pycbc import transforms from pycbc.io import record + class Constraint(object): """Creates a constraint that evaluates to True if parameters obey the constraint and False if they do not. @@ -60,7 +64,46 @@ def _constraint(self, params): return params[self.constraint_arg] +class SupernovaeConvexHull(Constraint): + """Pre defined constraint for core-collapse waveforms that checks + whether a given set of coefficients lie within the convex hull of + the coefficients of the principal component basis vectors. + """ + name = "supernovae_convex_hull" + required_parameters = ["coeff_0", "coeff_1"] + + def __init__(self, constraint_arg, transforms=None, **kwargs): + super(SupernovaeConvexHull, + self).__init__(constraint_arg, transforms=transforms, **kwargs) + + if 'principal_components_file' in kwargs: + pc_filename = kwargs['principal_components_file'] + hull_dimention = numpy.array(kwargs['hull_dimention']) + self.hull_dimention = int(hull_dimention) + pc_file = h5py.File(pc_filename, 'r') + pc_coefficients = numpy.array(pc_file.get('coefficients')) + pc_file.close() + hull_points = [] + for dim in range(self.hull_dimention): + hull_points.append(pc_coefficients[:, dim]) + hull_points = numpy.array(hull_points).T + pc_coeffs_hull = scipy.spatial.Delaunay(hull_points) + self._hull = pc_coeffs_hull + + def _constraint(self, params): + + output_array = [] + points = numpy.array([params["coeff_0"], + params["coeff_1"], + params["coeff_2"]]) + for coeff_index in range(len(params["coeff_0"])): + point = points[:, coeff_index][:self.hull_dimention] + output_array.append(self._hull.find_simplex(point) >= 0) + return numpy.array(output_array) + + # list of all constraints constraints = { Constraint.name : Constraint, + SupernovaeConvexHull.name : SupernovaeConvexHull, } diff --git a/pycbc/waveform/generator.py b/pycbc/waveform/generator.py index 18b2f2893ba..c29e4c8c5e0 100644 --- a/pycbc/waveform/generator.py +++ b/pycbc/waveform/generator.py @@ -30,6 +30,7 @@ from . import waveform from .waveform import (NoWaveformError, FailedWaveformError) from . import ringdown +from . import supernovae from pycbc import filter from pycbc import transforms from pycbc.types import TimeSeries @@ -631,6 +632,16 @@ def generate(self, **kwargs): return h +class TDomainSupernovaeGenerator(BaseGenerator): + """Uses supernovae.py to create time domain core-collapse supernovae waveforms + using a set of Principal Components provided in a .hdf file. + """ + def __init__(self, variable_args=(), **frozen_params): + super(TDomainSupernovaeGenerator, + self).__init__(supernovae.get_corecollapse_bounce, + variable_args=variable_args, **frozen_params) + + class FDomainDetFrameGenerator(object): """Generates frequency-domain waveform in a specific frame. @@ -869,6 +880,11 @@ def select_waveform_generator(approximant): elif approximant == 'TdQNMfromFreqTau': return TDomainFreqTauRingdownGenerator + # check if supernovae waveform: + elif approximant in supernovae.supernovae_td_approximants: + if approximant == 'CoreCollapseBounce': + return TDomainSupernovaeGenerator + # otherwise waveform approximant is not supported else: raise ValueError("%s is not a valid approximant." % approximant) diff --git a/pycbc/waveform/supernovae.py b/pycbc/waveform/supernovae.py new file mode 100644 index 00000000000..0e274cb4da5 --- /dev/null +++ b/pycbc/waveform/supernovae.py @@ -0,0 +1,52 @@ +"""Generate core-collapse supernovae waveform for core bounce and +subsequent postbounce oscillations. +""" + +import numpy +import h5py +from pycbc.types import TimeSeries + +_pc_dict = {} + + +def get_corecollapse_bounce(**kwargs): + """ Generates core bounce and postbounce waveform by using principal + component basis vectors from a .hdf file. The waveform parameters are the + coefficients of the principal components and the distance. The number of + principal components used can also be varied. + """ + + try: + principal_components = _pc_dict['principal_components'] + except KeyError: + with h5py.File(kwargs['principal_components_file'], 'r') as pc_file: + principal_components = numpy.array(pc_file['principal_components']) + _pc_dict['principal_components'] = principal_components + + if 'coefficients_array' in kwargs: + coefficients_array = kwargs['coefficients_array'] + else: + coeffs_keys = [x for x in kwargs if x.startswith('coeff_')] + coeffs_keys = numpy.sort(numpy.array(coeffs_keys)) + coefficients_array = numpy.array([kwargs[x] for x in coeffs_keys]) + + no_of_pcs = int(kwargs['no_of_pcs']) + coefficients_array = coefficients_array[:no_of_pcs] + principal_components = principal_components[:no_of_pcs] + + pc_len = len(principal_components) + assert len(coefficients_array) == pc_len + + distance = kwargs['distance'] + mpc_conversion = 3.08567758128e+22 + distance *= mpc_conversion + + strain = numpy.dot(coefficients_array, principal_components) / distance + delta_t = kwargs['delta_t'] + outhp = TimeSeries(strain, delta_t=delta_t) + outhc = TimeSeries(numpy.zeros(len(strain)), delta_t=delta_t) + return outhp, outhc + + +# Approximant names ########################################################### +supernovae_td_approximants = {'CoreCollapseBounce': get_corecollapse_bounce}