Skip to content
Snippets Groups Projects
Commit a61b8c76 authored by Moritz Huebner's avatar Moritz Huebner
Browse files

Merge branch 'master' into project_restructuring

# Conflicts:
#	test/example_tests.py
parents 23e21661 0cf81748
No related branches found
No related tags found
1 merge request!55Project restructuring
Showing
with 19236 additions and 45 deletions
......@@ -9,9 +9,169 @@ for some specific set of parameters. In mathematical notation, the likelihood
can be generically written as :math:`\mathcal{L}(d| \theta)`. How this is
coded up will depend on the problem, but `tupak` expects all likelihood
objects to have a `parameters` attribute (a dictionary of key-value pairs) and
a `log_likelihood()` method.
a `log_likelihood()` method. In thie page, we'll discuss how to write your own
Likelihood, and the standard likelihoods in :code:`tupak`.
The default likelihood we use in the examples is `GravitationalWaveTransient`:
The simplest likelihood
-----------------------
To start with let's consider perhaps the simplest likelihood we could write
down, namely a Gaussian likelihood for a set of data :math:`\vec{x}=[x_1, x_2,
\ldots, x_N]`. The likelihood for a single data point, given the mean
:math:`\mu` and standard-deviation :math:`\sigma` is given by
.. math::
\mathcal{L}(x_i| \mu, \sigma) =
\frac{1}{\sqrt{2\pi\sigma^2}}\mathrm{exp}\left(
\frac{-(x_i - \mu)^2}{2\sigma^2}\right)
Then, the likelihood for all :math:`N` data points is
.. math::
\mathcal{L}(\vec{x}| \mu, \sigma) = \prod_{i=1}^N
\mathcal{L}(x_i| \mu, \sigma)
In practise, we implement the log-likelihood to avoid numerical overflow
errors. To code this up in :code:`tupak`, we would write a class like this::
class GaussianLikelihood(tupak.Likelihood):
def __init__(self, data):
"""
A very simple Gaussian likelihood
Parameters
----------
data: array_like
The data to analyse
"""
self.data = data
self.N = len(data)
self.parameters = {'mu': None, 'sigma': None}
def log_likelihood(self):
mu = self.parameters['mu']
sigma = self.parameters['sigma']
res = self.data - mu
return -0.5 * (np.sum((res / sigma)**2)
+ self.N*np.log(2*np.pi*sigma**2))
This demonstrates the two required features of a :code:`tupak`
:code:`Likelihood` object:
#. It has a `parameters` attribute (a dictionary with
keys for all the parameters, in this case, initialised to :code:`None`)
#. It has a :code:`log_likelihood` method which, when called returns the log
likelihood for all the data.
You can find an example that uses this likelihood `here <https://git.ligo.org/Monash/tupak/blob/master/examples/other_examples/gaussian_example.py>`_.
.. tip::
Note that the example above subclasses the :code:`tupak.Likelihood` base
class, this simply provides a few in built functions. We recommend you also
do this when writing your own likelihood.
General likelihood for fitting a function :math:`y(x)` to some data with known noise
------------------------------------------------------------------------------------
The previous example was rather simplistic, Let's now consider that we have some
dependent data :math:`\vec{y}=y_1, y_2, \ldots y_N$` measured at
:math:`\vec{x}=x_1, x_2, \ldots, x_N`. We believe that the data is generated
by additive Gaussian noise with a known variance :math:`sigma^2` and a function
:math:`y(x; \theta)` where :math:`\theta` are some unknown parameters; that is
.. math::
y_i = y(x_i; \theta) + n_i
where :math:`n_i` is drawn from a normal distribution with zero mean and
standard deviation :math:`sigma`. As such, :math:`y_i - y(x_i; \theta)`
itself will have a likelihood
.. math::
\mathcal{L}(y_i; x_i, \theta) =
\frac{1}{\sqrt{2\pi\sigma^{2}}}
\mathrm{exp}\left(\frac{-(y_i - y(x_i; \theta))^2}{2\sigma^2}\right)
As with the previous case, the likelihood for all the data is the produce over
the likelihood for each data point.
In :code:`tupak`, we can code this up as a likelihood in the following way::
class GaussianLikelihood(tupak.Likelihood):
def __init__(self, x, y, sigma, function):
"""
A general Gaussian likelihood - the parameters are inferred from the
arguments of function
Parameters
----------
x, y: array_like
The data to analyse
sigma: float
The standard deviation of the noise
function:
The python function to fit to the data. Note, this must take the
dependent variable as its first argument. The other arguments are
will require a prior and will be sampled over (unless a fixed
value is given).
"""
self.x = x
self.y = y
self.sigma = sigma
self.N = len(x)
self.function = function
# These lines of code infer the parameters from the provided function
parameters = inspect.getargspec(function).args
parameters.pop(0)
self.parameters = dict.fromkeys(parameters)
def log_likelihood(self):
res = self.y - self.function(self.x, **self.parameters)
return -0.5 * (np.sum((res / self.sigma)**2)
+ self.N*np.log(2*np.pi*self.sigma**2))
def noise_log_likelihood(self):
return -0.5 * (np.sum((self.y / self.sigma)**2)
+ self.N*np.log(2*np.pi*self.sigma**2))
This likelihood can be given any python function, the data (in the form of
:code:`x` and :code:`y`) and the standard deviation of the noise. The
parameters are inferred from the arguments to the :code:`function` argument,
for example if, when instatiating the likelihood you passed in a the following
function::
def f(x, a, b):
return x**2 + b
Then you would also need to provide priors for :code:`a` and :code:`b`. For
this likelihood, the first argument to :code:`function` is always assumed to
be the dependent variable.
.. note::
Here we have explicitly defined the :code:`noise_log_likelihood` method
as the case when there is no signal (i.e., :math:`y(x; \theta)=0`).
You can see an example of this likelihood in the `linear regression example
<https://git.ligo.org/Monash/tupak/blob/master/examples/other_examples/linear_regression.py>`_.
Likelihood for transient gravitational waves
--------------------------------------------
In the examples above, we show how to write your own likelihood. However, for
routine gravitational wave data analysis of transient events, we have in built
likelihoods. The default likelihood we use in the examples is
`GravitationalWaveTransient`:
.. autoclass:: tupak.likelihood.GravitationalWaveTransient
......@@ -19,6 +179,9 @@ We also provide a simpler likelihood
.. autoclass:: tupak.likelihood.BasicGravitationalWaveTransient
Empty likelihood for subclassing
--------------------------------
We provide an empty parent class which can be subclassed for alternative use
cases
......
#!/bin/python
"""
An example of how to use tupak to perform paramater estimation for
non-gravitational wave data consisting of a Gaussian with a mean and variance
"""
from __future__ import division
import tupak
import numpy as np
# A few simple setup steps
tupak.utils.setup_logger()
label = 'gaussian_example'
outdir = 'outdir'
# Here is minimum requirement for a Likelihood class to run with tupak. In this
# case, we setup a GaussianLikelihood, which needs to have a log_likelihood
# method. Note, in this case we will NOT make use of the `tupak`
# waveform_generator to make the signal.
# Making simulated data: in this case, we consider just a Gaussian
data = np.random.normal(3, 4, 100)
class GaussianLikelihood(tupak.Likelihood):
def __init__(self, data):
"""
A very simple Gaussian likelihood
Parameters
----------
data: array_like
The data to analyse
"""
self.data = data
self.N = len(data)
self.parameters = {'mu': None, 'sigma': None}
def log_likelihood(self):
mu = self.parameters['mu']
sigma = self.parameters['sigma']
res = self.data - mu
return -0.5 * (np.sum((res / sigma)**2)
+ self.N*np.log(2*np.pi*sigma**2))
likelihood = GaussianLikelihood(data)
priors = dict(mu=tupak.prior.Uniform(0, 5, 'mu'),
sigma=tupak.prior.Uniform(0, 10, 'sigma'))
# And run sampler
result = tupak.run_sampler(
likelihood=likelihood, priors=priors, sampler='dynesty', npoints=500,
walks=10, outdir=outdir, label=label)
result.plot_corner()
print(result)
#!/bin/python
"""
An example of how to use tupak to perform paramater estimation for
non-gravitational wave data
non-gravitational wave data. In this case, fitting a linear function to
data with background Gaussian noise
"""
from __future__ import division
import tupak
import numpy as np
import matplotlib.pyplot as plt
import inspect
# A few simple setup steps
tupak.utils.setup_logger()
label = 'linear-regression'
label = 'linear_regression'
outdir = 'outdir'
# Here is minimum requirement for a Likelihood class to run linear regression
# with tupak. In this case, we setup a GaussianLikelihood, which needs to have
# a log_likelihood method. Note, in this case we make use of the `tupak`
# waveform_generator to make the signal (more on this later) But, one could
# make this work without the waveform generator.
# Making simulated data
# First, we define our signal model, in this case a simple linear function
# First, we define our "signal model", in this case a simple linear function
def model(time, m, c):
return time * m + c
......@@ -56,8 +51,10 @@ fig.savefig('{}/{}_data.png'.format(outdir, label))
class GaussianLikelihood(tupak.Likelihood):
def __init__(self, x, y, sigma, waveform_generator):
def __init__(self, x, y, sigma, function):
"""
A general Gaussian likelihood - the parameters are inferred from the
arguments of function
Parameters
----------
......@@ -65,19 +62,25 @@ class GaussianLikelihood(tupak.Likelihood):
The data to analyse
sigma: float
The standard deviation of the noise
waveform_generator: `tupak.waveform_generator.WaveformGenerator`
An object which can generate the 'waveform', which in this case is
any arbitrary function
function:
The python function to fit to the data. Note, this must take the
dependent variable as its first argument. The other arguments are
will require a prior and will be sampled over (unless a fixed
value is given).
"""
self.x = x
self.y = y
self.sigma = sigma
self.N = len(x)
self.waveform_generator = waveform_generator
self.parameters = waveform_generator.parameters
self.function = function
# These lines of code infer the parameters from the provided function
parameters = inspect.getargspec(function).args
parameters.pop(0)
self.parameters = dict.fromkeys(parameters)
def log_likelihood(self):
res = self.y - self.waveform_generator.time_domain_strain()
res = self.y - self.function(self.x, **self.parameters)
return -0.5 * (np.sum((res / self.sigma)**2)
+ self.N*np.log(2*np.pi*self.sigma**2))
......@@ -86,18 +89,9 @@ class GaussianLikelihood(tupak.Likelihood):
+ self.N*np.log(2*np.pi*self.sigma**2))
# Here, we make a `tupak` waveform_generator. In this case, of course, the
# name doesn't make so much sense. But essentially this is an objects that
# can generate a signal. We give it information on how to make the time series
# and the model() we wrote earlier.
waveform_generator = tupak.WaveformGenerator(
time_duration=time_duration, sampling_frequency=sampling_frequency,
time_domain_source_model=model)
# Now lets instantiate a version of out GravitationalWaveTransient, giving it
# the time, data and waveform_generator
likelihood = GaussianLikelihood(time, data, sigma, waveform_generator)
# Now lets instantiate a version of our GaussianLikelihood, giving it
# the time, data and signal model
likelihood = GaussianLikelihood(time, data, sigma, model)
# From hereon, the syntax is exactly equivalent to other tupak examples
# We make a prior
......@@ -109,5 +103,6 @@ priors['c'] = tupak.prior.Uniform(-2, 2, 'c')
result = tupak.run_sampler(
likelihood=likelihood, priors=priors, sampler='dynesty', npoints=500,
walks=10, injection_parameters=injection_parameters, outdir=outdir,
label=label, plot=True)
label=label)
result.plot_corner()
print(result)
#!/bin/python
"""
An example of how to use tupak to perform paramater estimation for
non-gravitational wave data. In this case, fitting a linear function to
data with background Gaussian noise with unknown variance.
"""
from __future__ import division
import tupak
import numpy as np
import matplotlib.pyplot as plt
import inspect
# A few simple setup steps
tupak.utils.setup_logger()
label = 'linear_regression_unknown_noise'
outdir = 'outdir'
# First, we define our "signal model", in this case a simple linear function
def model(time, m, c):
return time * m + c
# New we define the injection parameters which we make simulated data with
injection_parameters = dict(m=0.5, c=0.2)
# For this example, we'll inject standard Gaussian noise
sigma = 1
# These lines of code generate the fake data. Note the ** just unpacks the
# contents of the injection_paramsters when calling the model function.
sampling_frequency = 10
time_duration = 10
time = np.arange(0, time_duration, 1/sampling_frequency)
N = len(time)
data = model(time, **injection_parameters) + np.random.normal(0, sigma, N)
# We quickly plot the data to check it looks sensible
fig, ax = plt.subplots()
ax.plot(time, data, 'o', label='data')
ax.plot(time, model(time, **injection_parameters), '--r', label='signal')
ax.set_xlabel('time')
ax.set_ylabel('y')
ax.legend()
fig.savefig('{}/{}_data.png'.format(outdir, label))
# Parameter estimation: we now define a Gaussian Likelihood class relevant for
# our model.
class GaussianLikelihood(tupak.Likelihood):
def __init__(self, x, y, function, sigma=None):
"""
A general Gaussian likelihood for known or unknown noise - the model
parameters are inferred from the arguments of function
Parameters
----------
x, y: array_like
The data to analyse
function:
The python function to fit to the data. Note, this must take the
dependent variable as its first argument. The other arguments are
will require a prior and will be sampled over (unless a fixed
value is given).
sigma: None, float, array_like
If None, the standard deviation of the noise is unknown and will be
estimated (note: this requires a prior to be given for sigma). If
not None, this defined the standard-deviation of the data points.
This can either be a single float, or an array with length equal
to that for `x` and `y`.
"""
self.x = x
self.y = y
self.N = len(x)
self.function = function
# These lines of code infer the parameters from the provided function
parameters = inspect.getargspec(function).args
parameters.pop(0)
self.parameters = dict.fromkeys(parameters)
self.function_keys = self.parameters.keys()
self.parameters['sigma'] = None
def log_likelihood(self):
model_parameters = {k: self.parameters[k] for k in self.function_keys}
res = self.y - self.function(self.x, **model_parameters)
sigma = self.parameters['sigma']
return -0.5 * (np.sum((res / sigma)**2)
+ self.N*np.log(2*np.pi*sigma**2))
def noise_log_likelihood(self):
return np.nan
sigma = self.parameters['sigma']
return -0.5 * (np.sum((self.y / sigma)**2)
+ self.N*np.log(2*np.pi*sigma**2))
# Now lets instantiate a version of our GaussianLikelihood, giving it
# the time, data and signal model
likelihood = GaussianLikelihood(time, data, model)
# From hereon, the syntax is exactly equivalent to other tupak examples
# We make a prior
priors = {}
priors['m'] = tupak.prior.Uniform(0, 5, 'm')
priors['c'] = tupak.prior.Uniform(-2, 2, 'c')
priors['sigma'] = tupak.prior.Uniform(0, 10, 'sigma')
# And run sampler
result = tupak.run_sampler(
likelihood=likelihood, priors=priors, sampler='dynesty', npoints=500,
walks=10, injection_parameters=injection_parameters, outdir=outdir,
label=label)
result.plot_corner()
print(result)
#!/bin/python
"""
Tutorial to demonstrate running parameter estimation on a sine gaussian injected signal.
"""
from __future__ import division, print_function
import tupak
import numpy as np
# Set the duration and sampling frequency of the data segment that we're going to inject the signal into
time_duration = 4.
sampling_frequency = 2048.
# Specify the output directory and the name of the simulation.
outdir = 'outdir'
label = 'sine_gaussian'
tupak.utils.setup_logger(outdir=outdir, label=label)
# Set up a random seed for result reproducibility. This is optional!
np.random.seed(170801)
# We are going to inject a sine gaussian waveform. We first establish a dictionary of parameters that
# includes all of the different waveform parameters
injection_parameters = dict(hrss = 1e-22, Q = 5.0, frequency = 200.0, ra = 1.375, dec = -1.2108,
geocent_time = 1126259642.413, psi= 2.659)
# Create the waveform_generator using a sine Gaussian source function
waveform_generator = tupak.waveform_generator.WaveformGenerator(time_duration=time_duration,
sampling_frequency=sampling_frequency,
frequency_domain_source_model=tupak.source.sinegaussian,
parameters=injection_parameters)
hf_signal = waveform_generator.frequency_domain_strain()
# Set up interferometers. In this case we'll use three interferometers (LIGO-Hanford (H1), LIGO-Livingston (L1),
# and Virgo (V1)). These default to their design sensitivity
IFOs = [tupak.detector.get_interferometer_with_fake_noise_and_injection(
name, injection_polarizations=hf_signal, injection_parameters=injection_parameters, time_duration=time_duration,
sampling_frequency=sampling_frequency, outdir=outdir) for name in ['H1', 'L1', 'V1']]
# Set up prior, which is a dictionary
priors = dict()
# By default we will sample all terms in the signal models. However, this will take a long time for the calculation,
# so for this example we will set almost all of the priors to be equall to their injected values. This implies the
# prior is a delta function at the true, injected value. In reality, the sampler implementation is smart enough to
# not sample any parameter that has a delta-function prior.
for key in ['psi', 'ra', 'dec', 'geocent_time']:
priors[key] = injection_parameters[key]
# The above list does *not* include frequency and Q, which means those are the parameters
# that will be included in the sampler. If we do nothing, then the default priors get used.
#priors['Q'] = tupak.prior.create_default_prior(name='Q')
#priors['frequency'] = tupak.prior.create_default_prior(name='frequency')
priors['Q'] = tupak.prior.Uniform(2, 50, 'Q')
priors['frequency'] = tupak.prior.Uniform(30, 1000, 'frequency')
priors['hrss'] = tupak.prior.Uniform(1e-23, 1e-21, 'hrss')
# Initialise the likelihood by passing in the interferometer data (IFOs) and the waveoform generator
likelihood = tupak.likelihood.GravitationalWaveTransient(interferometers=IFOs, waveform_generator=waveform_generator)
# Run sampler. In this case we're going to use the `dynesty` sampler
result = tupak.sampler.run_sampler(likelihood=likelihood, priors=priors, sampler='dynesty', npoints=1000,
injection_parameters=injection_parameters, outdir=outdir, label=label)
# make some plots of the outputs
result.plot_corner()
print(result)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#!/bin/python
"""
Tutorial to demonstrate running parameter estimation/model selection on an NR
supernova injected signal. Signal model is made by applying PCA to a set of
supernova waveforms. The first few PCs are then linearly combined with a scale
factor. (See https://arxiv.org/pdf/1202.3256.pdf)
"""
from __future__ import division, print_function
import tupak
import numpy as np
# Set the duration and sampling frequency of the data segment that we're going to inject the signal into
time_duration = 3.
sampling_frequency = 4096.
# Specify the output directory and the name of the simulation.
outdir = 'outdir'
label = 'supernova'
tupak.utils.setup_logger(outdir=outdir, label=label)
# Set up a random seed for result reproducibility. This is optional!
np.random.seed(170801)
# We are going to inject a supernova waveform. We first establish a dictionary
# of parameters that includes all of the different waveform parameters. It will
# read in a signal to inject from a txt file.
injection_parameters = dict(file_path='MuellerL15_example_inj.txt',
luminosity_distance=10.0, ra=1.375,
dec=-1.2108, geocent_time=1126259642.413,
psi=2.659)
# Create the waveform_generator using a supernova source function
waveform_generator = tupak.waveform_generator.WaveformGenerator(
time_duration=time_duration, sampling_frequency=sampling_frequency,
frequency_domain_source_model=tupak.source.supernova,
parameters=injection_parameters)
hf_signal = waveform_generator.frequency_domain_strain()
# Set up interferometers. In this case we'll use three interferometers
# (LIGO-Hanford (H1), LIGO-Livingston (L1), and Virgo (V1)). These default to
# their design sensitivity
IFOs = [tupak.detector.get_interferometer_with_fake_noise_and_injection(
name, injection_polarizations=hf_signal,
injection_parameters=injection_parameters, time_duration=time_duration,
sampling_frequency=sampling_frequency, outdir=outdir)
for name in ['H1', 'L1', 'V1']]
# read in from a file the PCs used to create the signal model.
realPCs = np.loadtxt('SupernovaRealPCs.txt')
imagPCs = np.loadtxt('SupernovaImagPCs.txt')
# Now we make another waveform_generator because the signal model is
# not the same as the injection in this case.
simulation_parameters = dict(
realPCs=realPCs, imagPCs=imagPCs)
search_waveform_generator = tupak.waveform_generator.WaveformGenerator(
time_duration=time_duration, sampling_frequency=sampling_frequency,
frequency_domain_source_model=tupak.source.supernova_pca_model,
parameters=simulation_parameters)
# Set up prior
priors = dict()
for key in ['psi', 'geocent_time']:
priors[key] = injection_parameters[key]
priors['luminosity_distance'] = tupak.prior.Uniform(
2, 20, 'luminosity_distance')
priors['pc_coeff1'] = tupak.prior.Uniform(-1, 1, 'pc_coeff1')
priors['pc_coeff2'] = tupak.prior.Uniform(-1, 1, 'pc_coeff2')
priors['pc_coeff3'] = tupak.prior.Uniform(-1, 1, 'pc_coeff3')
priors['pc_coeff4'] = tupak.prior.Uniform(-1, 1, 'pc_coeff4')
priors['pc_coeff5'] = tupak.prior.Uniform(-1, 1, 'pc_coeff5')
priors['ra'] = tupak.prior.create_default_prior(name='ra')
priors['dec'] = tupak.prior.create_default_prior(name='dec')
priors['geocent_time'] = tupak.prior.Uniform(
injection_parameters['geocent_time'] - 1,
injection_parameters['geocent_time'] + 1,
'geocent_time')
# Initialise the likelihood by passing in the interferometer data (IFOs) and
# the waveoform generator
likelihood = tupak.GravitationalWaveTransient(
interferometers=IFOs, waveform_generator=search_waveform_generator)
# Run sampler.
result = tupak.run_sampler(
likelihood=likelihood, priors=priors, sampler='dynesty', npoints=500,
outdir=outdir, label=label)
# make some plots of the outputs
result.plot_corner()
print(result)
......@@ -7,7 +7,10 @@ import logging
# Required to run the tests
from past.builtins import execfile
from test.context import tupak
# Imported to ensure the examples run
import numpy as np
import inspect
tupak.utils.command_line_args.test = True
......
......@@ -322,8 +322,9 @@ class Interferometer(object):
return self.power_spectral_density.power_spectral_density_interpolated(self.frequency_array)
def set_data(self, sampling_frequency, duration, epoch=0,
from_power_spectral_density=False,
frequency_domain_strain=None):
from_power_spectral_density=False, zero_noise=False,
frequency_domain_strain=None, frame_file=None,
channel_name=None, overwrite_psd=True, **kwargs):
"""
Set the interferometer frequency-domain stain and accompanying PSD values.
......@@ -340,6 +341,18 @@ class Interferometer(object):
from_power_spectral_density: bool
If frequency_domain_strain not given, use IFO's PSD object to
generate noise
zero_noise: bool
If true and frequency_domain_strain and from_power_spectral_density
are false, set the data to be zero.
frame_file: str
File from which to load data.
channel_name: str
Channel to read from frame.
overwrite_psd: bool
Whether to overwrite the psd in the interferometer with one calculated
from the loaded data, default=True.
kwargs: dict
Additional arguments for loading data.
"""
self.epoch = epoch
......@@ -348,13 +361,25 @@ class Interferometer(object):
logging.info(
'Setting {} data using provided frequency_domain_strain'.format(self.name))
frequencies = utils.create_frequency_series(sampling_frequency, duration)
elif from_power_spectral_density is True:
elif from_power_spectral_density:
logging.info(
'Setting {} data using noise realization from provided'
'power_spectal_density'.format(self.name))
frequency_domain_strain, frequencies = \
self.power_spectral_density.get_noise_realisation(
sampling_frequency, duration)
elif zero_noise:
logging.info('Setting zero noise in {}'.format(self.name))
frequencies = utils.create_fequency_series(sampling_frequency, duration)
frequency_domain_strain = np.zeros_like(frequencies) * (1 + 1j)
elif frame_file is not None:
logging.info('Reading data from frame, {}.'.format(self.name))
strain = tupak.utils.read_frame_file(
frame_file, t1=epoch, t2=epoch+duration, channel=channel_name, resample=sampling_frequency)
frequency_domain_strain, frequencies = tupak.utils.process_strain_data(strain, **kwargs)
if overwrite_psd:
self.power_spectral_density = PowerSpectralDensity(
frame_file=frame_file, channel_name=channel_name, epoch=epoch, **kwargs)
else:
raise ValueError("No method to set data provided.")
......@@ -406,16 +431,33 @@ class Interferometer(object):
class PowerSpectralDensity:
def __init__(self, asd_file=None, psd_file='aLIGO_ZERO_DET_high_P_psd.txt'):
def __init__(self, asd_file=None, psd_file='aLIGO_ZERO_DET_high_P_psd.txt', frame_file=None, epoch=0,
psd_duration=1024, psd_offset=16, channel_name=None, filter_freq=1024, alpha=0.25, fft_length=4):
"""
Instantiate a new PowerSpectralDensity object.
Only one of the asd_file or psd_file needs to be specified.
If multiple are given, the first will be used.
FIXME: Allow reading a frame and then FFT to get PSD, use gwpy?
:param asd_file: amplitude spectral density, format 'f h_f'
:param psd_file: power spectral density, format 'f h_f'
Parameters:
asd_file: str
File containing amplitude spectral density, format 'f h_f'
psd_file: str
File containing power spectral density, format 'f h_f'
frame_file: str
Frame file to read data from.
epoch: float
Beginning of segment to analyse.
psd_duration: float
Duration of data to generate PSD from.
psd_offset: float
Offset of data from beginning of analysed segment.
channel_name: str
Name of channel to use to generate PSD.
alpha: float
Parameter for Tukey window.
fft_length: float
Number of seconds in a single fft.
"""
self.frequencies = []
......@@ -434,6 +476,24 @@ class PowerSpectralDensity:
logging.warning("The minimum of the provided curve is {:.2e}.".format(
min(self.amplitude_spectral_density)))
logging.warning("You may have intended to provide this as a power spectral density.")
elif frame_file is not None:
strain = tupak.utils.read_frame_file(frame_file, t1=epoch - psd_duration - psd_offset,
t2=epoch - psd_duration, channel=channel_name)
sampling_frequency = int(strain.sample_rate.value)
# Low pass filter
bp = filter_design.lowpass(filter_freq, strain.sample_rate)
strain = strain.filter(bp, filtfilt=True)
strain = strain.crop(*strain.span.contract(1))
# Create and save PSDs
NFFT = int(sampling_frequency * fft_length)
window = signal.windows.tukey(NFFT, alpha=alpha)
psd = strain.psd(fftlength=fft_length, window=window)
self.frequencies = psd.frequencies
self.power_spectral_density = psd.value
self.amplitude_spectral_density = self.power_spectral_density**0.5
self.interpolate_power_spectral_density()
else:
self.power_spectral_density_file = psd_file
self.import_power_spectral_density()
......@@ -666,7 +726,7 @@ def get_interferometer_with_open_data(
def get_interferometer_with_fake_noise_and_injection(
name, injection_polarizations, injection_parameters,
sampling_frequency=4096, time_duration=4, outdir='outdir', plot=True,
save=True):
save=True, zero_noise=False):
"""
Helper function to obtain an Interferometer instance with appropriate
power spectral density and data, given an center_time.
......@@ -690,6 +750,8 @@ def get_interferometer_with_fake_noise_and_injection(
If true, create an ASD + strain plot
save: bool
If true, save frequency domain data and PSD to file
zero_noise: bool
If true, set noise to zero.
Returns
-------
......@@ -701,9 +763,16 @@ def get_interferometer_with_fake_noise_and_injection(
utils.check_directory_exists_and_if_not_mkdir(outdir)
interferometer = get_empty_interferometer(name)
interferometer.set_data(
sampling_frequency=sampling_frequency, duration=time_duration,
from_power_spectral_density=True, epoch=(injection_parameters['geocent_time']+2)-time_duration)
if zero_noise:
interferometer.set_data(
sampling_frequency=sampling_frequency, duration=time_duration,
zero_noise=True,
epoch=(injection_parameters['geocent_time']+2)-time_duration)
else:
interferometer.set_data(
sampling_frequency=sampling_frequency, duration=time_duration,
from_power_spectral_density=True,
epoch=(injection_parameters['geocent_time']+2)-time_duration)
interferometer.inject_signal(
waveform_polarizations=injection_polarizations,
parameters=injection_parameters)
......
......@@ -154,7 +154,7 @@ class Result(dict):
kwargs['truths'] = kwargs.pop('truth')
if getattr(self, 'injection_parameters', None) is not None:
injection_parameters = [self.injection_parameters[key]
injection_parameters = [self.injection_parameters.get(key, None)
for key in self.search_parameter_keys]
kwargs['truths'] = kwargs.get('truths', injection_parameters)
......
from __future__ import division, print_function
import logging
import numpy as np
try:
import lalsimulation as lalsim
......@@ -59,6 +60,67 @@ def lal_binary_black_hole(
return {'plus': h_plus, 'cross': h_cross}
def sinegaussian(
frequency_array, hrss, Q, frequency, ra, dec, geocent_time, psi):
tau = Q / (np.sqrt(2.0)*np.pi*frequency)
temp = Q / (4.0*np.sqrt(np.pi)*frequency)
t = geocent_time
fm = frequency_array - frequency
fp = frequency_array + frequency
h_plus = ((hrss / np.sqrt(temp * (1+np.exp(-Q**2))))
* ((np.sqrt(np.pi)*tau)/2.0)
* (np.exp(-fm**2 * np.pi**2 * tau**2)
+ np.exp(-fp**2 * np.pi**2 * tau**2)))
h_cross = (-1j*(hrss / np.sqrt(temp * (1-np.exp(-Q**2))))
* ((np.sqrt(np.pi)*tau)/2.0)
* (np.exp(-fm**2 * np.pi**2 * tau**2)
- np.exp(-fp**2 * np.pi**2 * tau**2)))
return{'plus': h_plus, 'cross': h_cross}
def supernova(
frequency_array, realPCs, imagPCs, file_path, luminosity_distance, ra,
dec, geocent_time, psi):
""" A supernova NR simulation for injections """
realhplus, imaghplus, realhcross, imaghcross = np.loadtxt(
file_path, usecols=(0, 1, 2, 3), unpack=True)
# waveform in file at 10kpc
scaling = 1e-3 * (10.0 / luminosity_distance)
h_plus = scaling * (realhplus + 1.0j*imaghplus)
h_cross = scaling * (realhcross + 1.0j*imaghcross)
return {'plus': h_plus, 'cross': h_cross}
def supernova_pca_model(
frequency_array, realPCs, imagPCs, pc_coeff1, pc_coeff2, pc_coeff3,
pc_coeff4, pc_coeff5, luminosity_distance, ra, dec, geocent_time, psi):
""" Supernova signal model """
pc1 = realPCs[:, 0] + 1.0j*imagPCs[:, 0]
pc2 = realPCs[:, 1] + 1.0j*imagPCs[:, 1]
pc3 = realPCs[:, 2] + 1.0j*imagPCs[:, 2]
pc4 = realPCs[:, 3] + 1.0j*imagPCs[:, 3]
pc5 = realPCs[:, 4] + 1.0j*imagPCs[:, 5]
# file at 10kpc
scaling = 1e-23 * (10.0 / luminosity_distance)
h_plus = scaling * (pc_coeff1*pc1 + pc_coeff2*pc2 + pc_coeff3*pc3
+ pc_coeff4*pc4 + pc_coeff5*pc5)
h_cross = scaling * (pc_coeff1*pc1 + pc_coeff2*pc2 + pc_coeff3*pc3
+ pc_coeff4*pc4 + pc_coeff5*pc5)
return {'plus': h_plus, 'cross': h_cross}
#class BinaryNeutronStarMergerNumericalRelativity:
# """Loads in NR simulations of BNS merger
# takes parameters mean_mass, mass_ratio and equation_of_state, directory_path
......
......@@ -4,6 +4,8 @@ import os
import numpy as np
from math import fmod
from gwpy.timeseries import TimeSeries
from gwpy.signal import filter_design
from scipy import signal
import argparse
# Constants
......@@ -514,6 +516,112 @@ def get_open_strain_data(
return strain
def read_frame_file(file_name, t1, t2, channel=None, **kwargs):
""" A function which accesses the open strain data
This uses `gwpy` to download the open data and then saves a cached copy for
later use
Parameters
----------
file_name: str
The name of the frame to read
t1, t2: float
The GPS time of the start and end of the data
channel: str
The name of the channel being searched for, some standard channel names are attempted
if channel is not specified or if specified channel is not found.
**kwargs:
Passed to `gwpy.timeseries.TimeSeries.fetch_open_data`
Returns
-----------
strain: gwpy.timeseries.TimeSeries
"""
loaded = False
if channel is not None:
try:
strain = TimeSeries.read(source=file_name, channel=channel, start=t1, end=t2, **kwargs)
loaded = True
logging.info('Successfully loaded {}.'.format(channel))
except RuntimeError:
logging.warning('Channel {} not found. Trying preset channel names'.format(channel))
for channel_type in ['GDS-CALIB_STRAIN', 'DCS-CALIB_STRAIN_C01', 'DCS-CALIB_STRAIN_C02']:
for ifo_name in ['H1', 'L1']:
channel = '{}:{}'.format(ifo_name, channel_type)
if loaded:
continue
try:
strain = TimeSeries.read(source=file_name, channel=channel, start=t1, end=t2, **kwargs)
loaded = True
logging.info('Successfully loaded {}.'.format(channel))
except RuntimeError:
None
if loaded:
return strain
else:
logging.warning('No data loaded.')
return None
def process_strain_data(
strain, alpha=0.25, filter_freq=1024, **kwargs):
"""
Helper function to obtain an Interferometer instance with appropriate
PSD and data, given an center_time.
Parameters
----------
name: str
Detector name, e.g., 'H1'.
center_time: float
GPS time of the center_time about which to perform the analysis.
Note: the analysis data is from `center_time-T/2` to `center_time+T/2`.
T: float
The total time (in seconds) to analyse. Defaults to 4s.
alpha: float
The tukey window shape parameter passed to `scipy.signal.tukey`.
psd_offset, psd_duration: float
The power spectral density (psd) is estimated using data from
`center_time+psd_offset` to `center_time+psd_offset + psd_duration`.
outdir: str
Directory where the psd files are saved
plot: bool
If true, create an ASD + strain plot
filter_freq: float
Low pass filter frequency
**kwargs:
All keyword arguments are passed to
`gwpy.timeseries.TimeSeries.fetch_open_data()`.
Returns
-------
interferometer: `tupak.detector.Interferometer`
An Interferometer instance with a PSD and frequency-domain strain data.
"""
sampling_frequency = int(strain.sample_rate.value)
# Low pass filter
bp = filter_design.lowpass(filter_freq, strain.sample_rate)
strain = strain.filter(bp, filtfilt=True)
strain = strain.crop(*strain.span.contract(1))
time_series = strain.times.value
time_duration = time_series[-1] - time_series[0]
# Apply Tukey window
N = len(time_series)
strain = strain * signal.windows.tukey(N, alpha=alpha)
frequency_domain_strain, frequencies = nfft(strain.value, sampling_frequency)
return frequency_domain_strain, frequencies
def set_up_command_line_arguments():
parser = argparse.ArgumentParser(
description="Command line interface for tupak scripts")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment