From 1596825bfac9893f4b92fdad5e99aa267a1addcf Mon Sep 17 00:00:00 2001
From: Moritz <email@moritz-huebner.de>
Date: Wed, 1 May 2019 18:17:31 +1000
Subject: [PATCH] Moved some functions to prevent circular dependencies

---
 bilby/gw/detector/__init__.py       | 372 +---------------------------
 bilby/gw/detector/interferometer.py | 310 ++++++++++++++++++++++-
 bilby/gw/detector/networks.py       |  67 ++++-
 bilby/gw/detector/psd.py            |   2 +-
 4 files changed, 378 insertions(+), 373 deletions(-)

diff --git a/bilby/gw/detector/__init__.py b/bilby/gw/detector/__init__.py
index 0cb914470..444ce2a00 100644
--- a/bilby/gw/detector/__init__.py
+++ b/bilby/gw/detector/__init__.py
@@ -1,4 +1,4 @@
-from bilby.core.utils import logger
+from bilby.gw.detector import get_empty_interferometer, get_interferometer_with_open_data
 from .. import conversion
 from .interferometer import *
 from .networks import *
@@ -13,375 +13,7 @@ except ImportError:
                    " not be able to use some of the prebuilt functions.")
 
 
-def get_empty_interferometer(name):
-    """
-    Get an interferometer with standard parameters for known detectors.
-
-    These objects do not have any noise instantiated.
-
-    The available instruments are:
-        H1, L1, V1, GEO600, CE
-
-    Detector positions taken from:
-        L1/H1: LIGO-T980044-10
-        V1/GEO600: arXiv:gr-qc/0008066 [45]
-        CE: located at the site of H1
-
-    Detector sensitivities:
-        H1/L1/V1: https://dcc.ligo.org/LIGO-P1200087-v42/public
-        GEO600: http://www.geo600.org/1032083/GEO600_Sensitivity_Curves
-        CE: https://dcc.ligo.org/LIGO-P1600143/public
-
-
-    Parameters
-    ----------
-    name: str
-        Interferometer identifier.
-
-    Returns
-    -------
-    interferometer: Interferometer
-        Interferometer instance
-    """
-    filename = os.path.join(os.path.dirname(__file__), 'detectors', '{}.interferometer'.format(name))
-    try:
-        return load_interferometer(filename)
-    except OSError:
-        raise ValueError('Interferometer {} not implemented'.format(name))
-
-
-def load_interferometer(filename):
-    """Load an interferometer from a file."""
-    parameters = dict()
-    with open(filename, 'r') as parameter_file:
-        lines = parameter_file.readlines()
-        for line in lines:
-            if line[0] == '#':
-                continue
-            split_line = line.split('=')
-            key = split_line[0].strip()
-            value = eval('='.join(split_line[1:]))
-            parameters[key] = value
-    if 'shape' not in parameters.keys():
-        interferometer = Interferometer(**parameters)
-        logger.debug('Assuming L shape for {}'.format('name'))
-    elif parameters['shape'].lower() in ['l', 'ligo']:
-        parameters.pop('shape')
-        interferometer = Interferometer(**parameters)
-    elif parameters['shape'].lower() in ['triangular', 'triangle']:
-        parameters.pop('shape')
-        interferometer = TriangularInterferometer(**parameters)
-    else:
-        raise IOError("{} could not be loaded. Invalid parameter 'shape'.".format(filename))
-    return interferometer
-
-
-def get_interferometer_with_open_data(
-        name, trigger_time, duration=4, start_time=None, roll_off=0.2,
-        psd_offset=-1024, psd_duration=100, cache=True, outdir='outdir',
-        label=None, plot=True, filter_freq=None, **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'.
-    trigger_time: float
-        Trigger GPS time.
-    duration: float, optional
-        The total time (in seconds) to analyse. Defaults to 4s.
-    start_time: float, optional
-        Beginning of the segment, if None, the trigger is placed 2s before the end
-        of the segment.
-    roll_off: float
-        The roll-off (in seconds) used in the Tukey window.
-    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`.
-    cache: bool, optional
-        Whether or not to store the acquired data
-    outdir: str
-        Directory where the psd files are saved
-    label: str
-        If given, an identifying label used in generating file names.
-    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
-    -------
-    bilby.gw.detector.Interferometer: An Interferometer instance with a PSD and frequency-domain strain data.
-
-    """
-
-    logger.warning(
-        "Parameter estimation for real interferometer data in bilby is in "
-        "alpha testing at the moment: the routines for windowing and filtering"
-        " have not been reviewed.")
-
-    utils.check_directory_exists_and_if_not_mkdir(outdir)
-
-    if start_time is None:
-        start_time = trigger_time + 2 - duration
-
-    strain = InterferometerStrainData(roll_off=roll_off)
-    strain.set_from_open_data(
-        name=name, start_time=start_time, duration=duration,
-        outdir=outdir, cache=cache, **kwargs)
-    strain.low_pass_filter(filter_freq)
-
-    strain_psd = InterferometerStrainData(roll_off=roll_off)
-    strain_psd.set_from_open_data(
-        name=name, start_time=start_time + duration + psd_offset,
-        duration=psd_duration, outdir=outdir, cache=cache, **kwargs)
-    # Low pass filter
-    strain_psd.low_pass_filter(filter_freq)
-    # Create and save PSDs
-    psd_frequencies, psd_array = strain_psd.create_power_spectral_density(
-        name=name, outdir=outdir, fft_length=strain.duration)
-
-    interferometer = get_empty_interferometer(name)
-    interferometer.power_spectral_density = PowerSpectralDensity(
-        psd_array=psd_array, frequency_array=psd_frequencies)
-    interferometer.strain_data = strain
-
-    if plot:
-        interferometer.plot_data(outdir=outdir, label=label)
-
-    return interferometer
-
-
-def get_interferometer_with_fake_noise_and_injection(
-        name, injection_parameters, injection_polarizations=None,
-        waveform_generator=None, sampling_frequency=4096, duration=4,
-        start_time=None, outdir='outdir', label=None, plot=True, save=True,
-        zero_noise=False):
-    """
-    Helper function to obtain an Interferometer instance with appropriate
-    power spectral density and data, given an center_time.
-
-    Note: by default this generates an Interferometer with a power spectral
-    density based on advanced LIGO.
-
-    Parameters
-    ----------
-    name: str
-        Detector name, e.g., 'H1'.
-    injection_parameters: dict
-        injection parameters, needed for sky position and timing
-    injection_polarizations: dict
-       Polarizations of waveform to inject, output of
-       `waveform_generator.frequency_domain_strain()`. If
-       `waveform_generator` is also given, the injection_polarizations will
-       be calculated directly and this argument can be ignored.
-    waveform_generator: bilby.gw.waveform_generator.WaveformGenerator
-        A WaveformGenerator instance using the source model to inject. If
-        `injection_polarizations` is given, this will be ignored.
-    sampling_frequency: float
-        sampling frequency for data, should match injection signal
-    duration: float
-        length of data, should be the same as used for signal generation
-    start_time: float
-        Beginning of data segment, if None, injection is placed 2s before
-        end of segment.
-    outdir: str
-        directory in which to store output
-    label: str
-        If given, an identifying label used in generating file names.
-    plot: bool
-        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
-    -------
-    bilby.gw.detector.Interferometer: An Interferometer instance with a PSD and frequency-domain strain data.
-
-    """
-
-    utils.check_directory_exists_and_if_not_mkdir(outdir)
-
-    if start_time is None:
-        start_time = injection_parameters['geocent_time'] + 2 - duration
-
-    interferometer = get_empty_interferometer(name)
-    interferometer.power_spectral_density = PowerSpectralDensity.from_aligo()
-    if zero_noise:
-        interferometer.set_strain_data_from_zero_noise(
-            sampling_frequency=sampling_frequency, duration=duration,
-            start_time=start_time)
-    else:
-        interferometer.set_strain_data_from_power_spectral_density(
-            sampling_frequency=sampling_frequency, duration=duration,
-            start_time=start_time)
-
-    injection_polarizations = interferometer.inject_signal(
-        parameters=injection_parameters,
-        injection_polarizations=injection_polarizations,
-        waveform_generator=waveform_generator)
-
-    signal = interferometer.get_detector_response(
-        injection_polarizations, injection_parameters)
-
-    if plot:
-        interferometer.plot_data(signal=signal, outdir=outdir, label=label)
-
-    if save:
-        interferometer.save_data(outdir, label=label)
-
-    return interferometer
-
-
-def get_event_data(
-        event, interferometer_names=None, duration=4, roll_off=0.2,
-        psd_offset=-1024, psd_duration=100, cache=True, outdir='outdir',
-        label=None, plot=True, filter_freq=None, **kwargs):
-    """
-    Get open data for a specified event.
-
-    Parameters
-    ----------
-    event: str
-        Event descriptor, this can deal with some prefixes, e.g., '150914',
-        'GW150914', 'LVT151012'
-    interferometer_names: list, optional
-        List of interferometer identifiers, e.g., 'H1'.
-        If None will look for data in 'H1', 'V1', 'L1'
-    duration: float
-        Time duration to search for.
-    roll_off: float
-        The roll-off (in seconds) used in the Tukey window.
-    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`.
-    cache: bool
-        Whether or not to store the acquired data.
-    outdir: str
-        Directory where the psd files are saved
-    label: str
-        If given, an identifying label used in generating file names.
-    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()`.
-
-    Return
-    ------
-    list: A list of bilby.gw.detector.Interferometer objects
-    """
-    event_time = gwutils.get_event_time(event)
-
-    interferometers = []
-
-    if interferometer_names is None:
-        interferometer_names = ['H1', 'L1', 'V1']
-
-    for name in interferometer_names:
-        try:
-            interferometers.append(get_interferometer_with_open_data(
-                name, trigger_time=event_time, duration=duration, roll_off=roll_off,
-                psd_offset=psd_offset, psd_duration=psd_duration, cache=cache,
-                outdir=outdir, label=label, plot=plot, filter_freq=filter_freq,
-                **kwargs))
-        except ValueError as e:
-            logger.debug("Error raised {}".format(e))
-            logger.warning('No data found for {}.'.format(name))
-
-    return InterferometerList(interferometers)
-
-
-def load_data_from_cache_file(
-        cache_file, start_time, segment_duration, psd_duration, psd_start_time,
-        channel_name=None, sampling_frequency=4096, roll_off=0.2,
-        overlap=0, outdir=None):
-    """ Helper routine to generate an interferometer from a cache file
-
-    Parameters
-    ----------
-    cache_file: str
-        Path to the location of the cache file
-    start_time, psd_start_time: float
-        GPS start time of the segment and data stretch used for the PSD
-    segment_duration, psd_duration: float
-        Segment duration and duration of data to use to generate the PSD (in
-        seconds).
-    roll_off: float, optional
-        Rise time in seconds of tukey window.
-    overlap: float,
-        Number of seconds of overlap between FFTs.
-    channel_name: str
-        Channel name
-    sampling_frequency: int
-        Sampling frequency
-    outdir: str, optional
-        The output directory in which the data is saved
-
-    Returns
-    -------
-    ifo: bilby.gw.detector.Interferometer
-        An initialised interferometer object with strain data set to the
-        appropriate data in the cache file and a PSD.
-    """
-
-    data_set = False
-    psd_set = False
-
-    with open(cache_file, 'r') as ff:
-        for line in ff:
-            cache = lal.utils.cache.CacheEntry(line)
-            data_in_cache = (
-                (cache.segment[0].gpsSeconds < start_time) &
-                (cache.segment[1].gpsSeconds > start_time + segment_duration))
-            psd_in_cache = (
-                (cache.segment[0].gpsSeconds < psd_start_time) &
-                (cache.segment[1].gpsSeconds > psd_start_time + psd_duration))
-            ifo = get_empty_interferometer(
-                "{}1".format(cache.observatory))
-            if not data_set & data_in_cache:
-                ifo.set_strain_data_from_frame_file(
-                    frame_file=cache.path,
-                    sampling_frequency=sampling_frequency,
-                    duration=segment_duration,
-                    start_time=start_time,
-                    channel=channel_name, buffer_time=0)
-                data_set = True
-            if not psd_set & psd_in_cache:
-                ifo.power_spectral_density = \
-                    PowerSpectralDensity.from_frame_file(
-                        cache.path,
-                        psd_start_time=psd_start_time,
-                        psd_duration=psd_duration,
-                        fft_length=segment_duration,
-                        sampling_frequency=sampling_frequency,
-                        roll_off=roll_off,
-                        overlap=overlap,
-                        channel=channel_name,
-                        name=cache.observatory,
-                        outdir=outdir,
-                        analysis_segment_start_time=start_time)
-                psd_set = True
-    if data_set and psd_set:
-        return ifo
-    elif not data_set:
-        raise ValueError('Data not loaded for {}'.format(ifo.name))
-    elif not psd_set:
-        raise ValueError('PSD not created for {}'.format(ifo.name))
-
-
-def get_safe_signal_duration(mass_1, mass_2, a_1, a_2, tilt_1, tilt_2, flow=10,
-                             **kwargs):
+def get_safe_signal_duration(mass_1, mass_2, a_1, a_2, tilt_1, tilt_2, flow=10):
     """ Calculate the safe signal duration, given the parameters
 
     Parameters
diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py
index c11e04c7b..43af4e73e 100644
--- a/bilby/gw/detector/interferometer.py
+++ b/bilby/gw/detector/interferometer.py
@@ -1,6 +1,7 @@
 import os
 import sys
 
+import lal
 import numpy as np
 from matplotlib import pyplot as plt
 
@@ -8,7 +9,8 @@ from bilby.core import utils
 from bilby.core.utils import logger
 from bilby.gw import utils as gwutils
 from bilby.gw.calibration import Recalibrate
-from bilby.gw.detector import InterferometerStrainData
+from bilby.gw.detector import TriangularInterferometer, InterferometerStrainData, PowerSpectralDensity
+from .strain_data import InterferometerStrainData
 
 try:
     import gwpy
@@ -931,3 +933,309 @@ class Interferometer(object):
         if res.__class__ != cls:
             raise TypeError('The loaded object is not an Interferometer')
         return res
+
+
+def get_empty_interferometer(name):
+    """
+    Get an interferometer with standard parameters for known detectors.
+
+    These objects do not have any noise instantiated.
+
+    The available instruments are:
+        H1, L1, V1, GEO600, CE
+
+    Detector positions taken from:
+        L1/H1: LIGO-T980044-10
+        V1/GEO600: arXiv:gr-qc/0008066 [45]
+        CE: located at the site of H1
+
+    Detector sensitivities:
+        H1/L1/V1: https://dcc.ligo.org/LIGO-P1200087-v42/public
+        GEO600: http://www.geo600.org/1032083/GEO600_Sensitivity_Curves
+        CE: https://dcc.ligo.org/LIGO-P1600143/public
+
+
+    Parameters
+    ----------
+    name: str
+        Interferometer identifier.
+
+    Returns
+    -------
+    interferometer: Interferometer
+        Interferometer instance
+    """
+    filename = os.path.join(os.path.dirname(__file__), 'detectors', '{}.interferometer'.format(name))
+    try:
+        return load_interferometer(filename)
+    except OSError:
+        raise ValueError('Interferometer {} not implemented'.format(name))
+
+
+def load_interferometer(filename):
+    """Load an interferometer from a file."""
+    parameters = dict()
+    with open(filename, 'r') as parameter_file:
+        lines = parameter_file.readlines()
+        for line in lines:
+            if line[0] == '#':
+                continue
+            split_line = line.split('=')
+            key = split_line[0].strip()
+            value = eval('='.join(split_line[1:]))
+            parameters[key] = value
+    if 'shape' not in parameters.keys():
+        interferometer = Interferometer(**parameters)
+        logger.debug('Assuming L shape for {}'.format('name'))
+    elif parameters['shape'].lower() in ['l', 'ligo']:
+        parameters.pop('shape')
+        interferometer = Interferometer(**parameters)
+    elif parameters['shape'].lower() in ['triangular', 'triangle']:
+        parameters.pop('shape')
+        interferometer = TriangularInterferometer(**parameters)
+    else:
+        raise IOError("{} could not be loaded. Invalid parameter 'shape'.".format(filename))
+    return interferometer
+
+
+def get_interferometer_with_open_data(
+        name, trigger_time, duration=4, start_time=None, roll_off=0.2,
+        psd_offset=-1024, psd_duration=100, cache=True, outdir='outdir',
+        label=None, plot=True, filter_freq=None, **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'.
+    trigger_time: float
+        Trigger GPS time.
+    duration: float, optional
+        The total time (in seconds) to analyse. Defaults to 4s.
+    start_time: float, optional
+        Beginning of the segment, if None, the trigger is placed 2s before the end
+        of the segment.
+    roll_off: float
+        The roll-off (in seconds) used in the Tukey window.
+    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`.
+    cache: bool, optional
+        Whether or not to store the acquired data
+    outdir: str
+        Directory where the psd files are saved
+    label: str
+        If given, an identifying label used in generating file names.
+    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
+    -------
+    bilby.gw.detector.Interferometer: An Interferometer instance with a PSD and frequency-domain strain data.
+
+    """
+
+    logger.warning(
+        "Parameter estimation for real interferometer data in bilby is in "
+        "alpha testing at the moment: the routines for windowing and filtering"
+        " have not been reviewed.")
+
+    utils.check_directory_exists_and_if_not_mkdir(outdir)
+
+    if start_time is None:
+        start_time = trigger_time + 2 - duration
+
+    strain = InterferometerStrainData(roll_off=roll_off)
+    strain.set_from_open_data(
+        name=name, start_time=start_time, duration=duration,
+        outdir=outdir, cache=cache, **kwargs)
+    strain.low_pass_filter(filter_freq)
+
+    strain_psd = InterferometerStrainData(roll_off=roll_off)
+    strain_psd.set_from_open_data(
+        name=name, start_time=start_time + duration + psd_offset,
+        duration=psd_duration, outdir=outdir, cache=cache, **kwargs)
+    # Low pass filter
+    strain_psd.low_pass_filter(filter_freq)
+    # Create and save PSDs
+    psd_frequencies, psd_array = strain_psd.create_power_spectral_density(
+        name=name, outdir=outdir, fft_length=strain.duration)
+
+    interferometer = get_empty_interferometer(name)
+    interferometer.power_spectral_density = PowerSpectralDensity(
+        psd_array=psd_array, frequency_array=psd_frequencies)
+    interferometer.strain_data = strain
+
+    if plot:
+        interferometer.plot_data(outdir=outdir, label=label)
+
+    return interferometer
+
+
+def get_interferometer_with_fake_noise_and_injection(
+        name, injection_parameters, injection_polarizations=None,
+        waveform_generator=None, sampling_frequency=4096, duration=4,
+        start_time=None, outdir='outdir', label=None, plot=True, save=True,
+        zero_noise=False):
+    """
+    Helper function to obtain an Interferometer instance with appropriate
+    power spectral density and data, given an center_time.
+
+    Note: by default this generates an Interferometer with a power spectral
+    density based on advanced LIGO.
+
+    Parameters
+    ----------
+    name: str
+        Detector name, e.g., 'H1'.
+    injection_parameters: dict
+        injection parameters, needed for sky position and timing
+    injection_polarizations: dict
+       Polarizations of waveform to inject, output of
+       `waveform_generator.frequency_domain_strain()`. If
+       `waveform_generator` is also given, the injection_polarizations will
+       be calculated directly and this argument can be ignored.
+    waveform_generator: bilby.gw.waveform_generator.WaveformGenerator
+        A WaveformGenerator instance using the source model to inject. If
+        `injection_polarizations` is given, this will be ignored.
+    sampling_frequency: float
+        sampling frequency for data, should match injection signal
+    duration: float
+        length of data, should be the same as used for signal generation
+    start_time: float
+        Beginning of data segment, if None, injection is placed 2s before
+        end of segment.
+    outdir: str
+        directory in which to store output
+    label: str
+        If given, an identifying label used in generating file names.
+    plot: bool
+        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
+    -------
+    bilby.gw.detector.Interferometer: An Interferometer instance with a PSD and frequency-domain strain data.
+
+    """
+
+    utils.check_directory_exists_and_if_not_mkdir(outdir)
+
+    if start_time is None:
+        start_time = injection_parameters['geocent_time'] + 2 - duration
+
+    interferometer = get_empty_interferometer(name)
+    interferometer.power_spectral_density = PowerSpectralDensity.from_aligo()
+    if zero_noise:
+        interferometer.set_strain_data_from_zero_noise(
+            sampling_frequency=sampling_frequency, duration=duration,
+            start_time=start_time)
+    else:
+        interferometer.set_strain_data_from_power_spectral_density(
+            sampling_frequency=sampling_frequency, duration=duration,
+            start_time=start_time)
+
+    injection_polarizations = interferometer.inject_signal(
+        parameters=injection_parameters,
+        injection_polarizations=injection_polarizations,
+        waveform_generator=waveform_generator)
+
+    signal = interferometer.get_detector_response(
+        injection_polarizations, injection_parameters)
+
+    if plot:
+        interferometer.plot_data(signal=signal, outdir=outdir, label=label)
+
+    if save:
+        interferometer.save_data(outdir, label=label)
+
+    return interferometer
+
+
+def load_data_from_cache_file(
+        cache_file, start_time, segment_duration, psd_duration, psd_start_time,
+        channel_name=None, sampling_frequency=4096, roll_off=0.2,
+        overlap=0, outdir=None):
+    """ Helper routine to generate an interferometer from a cache file
+
+    Parameters
+    ----------
+    cache_file: str
+        Path to the location of the cache file
+    start_time, psd_start_time: float
+        GPS start time of the segment and data stretch used for the PSD
+    segment_duration, psd_duration: float
+        Segment duration and duration of data to use to generate the PSD (in
+        seconds).
+    roll_off: float, optional
+        Rise time in seconds of tukey window.
+    overlap: float,
+        Number of seconds of overlap between FFTs.
+    channel_name: str
+        Channel name
+    sampling_frequency: int
+        Sampling frequency
+    outdir: str, optional
+        The output directory in which the data is saved
+
+    Returns
+    -------
+    ifo: bilby.gw.detector.Interferometer
+        An initialised interferometer object with strain data set to the
+        appropriate data in the cache file and a PSD.
+    """
+
+    data_set = False
+    psd_set = False
+
+    with open(cache_file, 'r') as ff:
+        for line in ff:
+            cache = lal.utils.cache.CacheEntry(line)
+            data_in_cache = (
+                (cache.segment[0].gpsSeconds < start_time) &
+                (cache.segment[1].gpsSeconds > start_time + segment_duration))
+            psd_in_cache = (
+                (cache.segment[0].gpsSeconds < psd_start_time) &
+                (cache.segment[1].gpsSeconds > psd_start_time + psd_duration))
+            ifo = get_empty_interferometer(
+                "{}1".format(cache.observatory))
+            if not data_set & data_in_cache:
+                ifo.set_strain_data_from_frame_file(
+                    frame_file=cache.path,
+                    sampling_frequency=sampling_frequency,
+                    duration=segment_duration,
+                    start_time=start_time,
+                    channel=channel_name, buffer_time=0)
+                data_set = True
+            if not psd_set & psd_in_cache:
+                ifo.power_spectral_density = \
+                    PowerSpectralDensity.from_frame_file(
+                        cache.path,
+                        psd_start_time=psd_start_time,
+                        psd_duration=psd_duration,
+                        fft_length=segment_duration,
+                        sampling_frequency=sampling_frequency,
+                        roll_off=roll_off,
+                        overlap=overlap,
+                        channel=channel_name,
+                        name=cache.observatory,
+                        outdir=outdir,
+                        analysis_segment_start_time=start_time)
+                psd_set = True
+    if data_set and psd_set:
+        return ifo
+    elif not data_set:
+        raise ValueError('Data not loaded for {}'.format(ifo.name))
+    elif not psd_set:
+        raise ValueError('PSD not created for {}'.format(ifo.name))
\ No newline at end of file
diff --git a/bilby/gw/detector/networks.py b/bilby/gw/detector/networks.py
index 7058f492e..8eac32390 100644
--- a/bilby/gw/detector/networks.py
+++ b/bilby/gw/detector/networks.py
@@ -4,7 +4,11 @@ import sys
 import numpy as np
 
 from bilby.core import utils
-from bilby.gw.detector import get_empty_interferometer, Interferometer, PowerSpectralDensity
+from bilby.core.utils import logger
+from bilby.gw import utils
+from bilby.gw.detector import get_interferometer_with_open_data
+from .interferometer import Interferometer
+from .psd import PowerSpectralDensity
 
 
 class InterferometerList(list):
@@ -261,3 +265,64 @@ class TriangularInterferometer(InterferometerList):
 
             latitude += np.arctan(length * np.sin(xarm_azimuth * np.pi / 180) * 1e3 / utils.radius_of_earth)
             longitude += np.arctan(length * np.cos(xarm_azimuth * np.pi / 180) * 1e3 / utils.radius_of_earth)
+
+
+def get_event_data(
+        event, interferometer_names=None, duration=4, roll_off=0.2,
+        psd_offset=-1024, psd_duration=100, cache=True, outdir='outdir',
+        label=None, plot=True, filter_freq=None, **kwargs):
+    """
+    Get open data for a specified event.
+
+    Parameters
+    ----------
+    event: str
+        Event descriptor, this can deal with some prefixes, e.g., '150914',
+        'GW150914', 'LVT151012'
+    interferometer_names: list, optional
+        List of interferometer identifiers, e.g., 'H1'.
+        If None will look for data in 'H1', 'V1', 'L1'
+    duration: float
+        Time duration to search for.
+    roll_off: float
+        The roll-off (in seconds) used in the Tukey window.
+    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`.
+    cache: bool
+        Whether or not to store the acquired data.
+    outdir: str
+        Directory where the psd files are saved
+    label: str
+        If given, an identifying label used in generating file names.
+    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()`.
+
+    Return
+    ------
+    list: A list of bilby.gw.detector.Interferometer objects
+    """
+    event_time = gwutils.get_event_time(event)
+
+    interferometers = []
+
+    if interferometer_names is None:
+        interferometer_names = ['H1', 'L1', 'V1']
+
+    for name in interferometer_names:
+        try:
+            interferometers.append(get_interferometer_with_open_data(
+                name, trigger_time=event_time, duration=duration, roll_off=roll_off,
+                psd_offset=psd_offset, psd_duration=psd_duration, cache=cache,
+                outdir=outdir, label=label, plot=plot, filter_freq=filter_freq,
+                **kwargs))
+        except ValueError as e:
+            logger.debug("Error raised {}".format(e))
+            logger.warning('No data found for {}.'.format(name))
+
+    return InterferometerList(interferometers)
\ No newline at end of file
diff --git a/bilby/gw/detector/psd.py b/bilby/gw/detector/psd.py
index 808e2a8ce..a0d30d6a4 100644
--- a/bilby/gw/detector/psd.py
+++ b/bilby/gw/detector/psd.py
@@ -5,7 +5,7 @@ from scipy.interpolate import interp1d
 
 from bilby.core import utils
 from bilby.core.utils import logger
-from bilby.gw.detector import InterferometerStrainData
+from .strain_data import InterferometerStrainData
 
 
 class PowerSpectralDensity(object):
-- 
GitLab