diff --git a/bilby/gw/detector.py b/bilby/gw/detector.py deleted file mode 100644 index febb6e41528cb0c5001c5e3d9c0eb8e166b45eb8..0000000000000000000000000000000000000000 --- a/bilby/gw/detector.py +++ /dev/null @@ -1,2597 +0,0 @@ -from __future__ import division, print_function, absolute_import - -import os -import sys - -import matplotlib.pyplot as plt -import numpy as np -from scipy.signal.windows import tukey -from scipy.interpolate import interp1d - -from . import utils as gwutils -from ..core import utils -from ..core.utils import logger -from ..core.series import CoupledTimeAndFrequencySeries -from .calibration import Recalibrate -from . import conversion - -try: - import gwpy - import gwpy.signal -except ImportError: - logger.warning("You do not have gwpy installed currently. You will " - " not be able to use some of the prebuilt functions.") - -try: - import lal - import lalsimulation as lalsim -except ImportError: - logger.warning("You do not have lalsuite installed currently. You will" - " not be able to use some of the prebuilt functions.") - - -class InterferometerList(list): - """ A list of Interferometer objects """ - - def __init__(self, interferometers): - """ Instantiate a InterferometerList - - The InterferometerList is a list of Interferometer objects, each - object has the data used in evaluating the likelihood - - Parameters - ---------- - interferometers: iterable - The list of interferometers - """ - - list.__init__(self) - if type(interferometers) == str: - raise TypeError("Input must not be a string") - for ifo in interferometers: - if type(ifo) == str: - ifo = get_empty_interferometer(ifo) - if type(ifo) not in [Interferometer, TriangularInterferometer]: - raise TypeError("Input list of interferometers are not all Interferometer objects") - else: - self.append(ifo) - self._check_interferometers() - - def _check_interferometers(self): - """ Check certain aspects of the set are the same """ - consistent_attributes = ['duration', 'start_time', 'sampling_frequency'] - for attribute in consistent_attributes: - x = [getattr(interferometer.strain_data, attribute) - for interferometer in self] - if not all(y == x[0] for y in x): - raise ValueError("The {} of all interferometers are not the same".format(attribute)) - - def set_strain_data_from_power_spectral_densities(self, sampling_frequency, duration, start_time=0): - """ Set the `Interferometer.strain_data` from the power spectral densities of the detectors - - This uses the `interferometer.power_spectral_density` object to set - the `strain_data` to a noise realization. See - `bilby.gw.detector.InterferometerStrainData` for further information. - - Parameters - ---------- - sampling_frequency: float - The sampling frequency (in Hz) - duration: float - The data duration (in s) - start_time: float - The GPS start-time of the data - - """ - for interferometer in self: - interferometer.set_strain_data_from_power_spectral_density(sampling_frequency=sampling_frequency, - duration=duration, - start_time=start_time) - - def set_strain_data_from_zero_noise(self, sampling_frequency, duration, start_time=0): - """ Set the `Interferometer.strain_data` from the power spectral densities of the detectors - - This uses the `interferometer.power_spectral_density` object to set - the `strain_data` to zero noise. See - `bilby.gw.detector.InterferometerStrainData` for further information. - - Parameters - ---------- - sampling_frequency: float - The sampling frequency (in Hz) - duration: float - The data duration (in s) - start_time: float - The GPS start-time of the data - - """ - for interferometer in self: - interferometer.set_strain_data_from_zero_noise(sampling_frequency=sampling_frequency, - duration=duration, - start_time=start_time) - - def inject_signal(self, parameters=None, injection_polarizations=None, waveform_generator=None): - """ Inject a signal into noise in each of the three detectors. - - Parameters - ---------- - parameters: dict - Parameters of the injection. - 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. - - Note - ---------- - if your signal takes a substantial amount of time to generate, or - you experience buggy behaviour. It is preferable to provide the - injection_polarizations directly. - - Returns - ------- - injection_polarizations: dict - - """ - if injection_polarizations is None: - if waveform_generator is not None: - injection_polarizations = \ - waveform_generator.frequency_domain_strain(parameters) - else: - raise ValueError( - "inject_signal needs one of waveform_generator or " - "injection_polarizations.") - - all_injection_polarizations = list() - for interferometer in self: - all_injection_polarizations.append( - interferometer.inject_signal(parameters=parameters, injection_polarizations=injection_polarizations)) - - return all_injection_polarizations - - def save_data(self, outdir, label=None): - """ Creates a save file for the data in plain text format - - Parameters - ---------- - outdir: str - The output directory in which the data is supposed to be saved - label: str - The string labelling the data - """ - for interferometer in self: - interferometer.save_data(outdir=outdir, label=label) - - def plot_data(self, signal=None, outdir='.', label=None): - if utils.command_line_args.test: - return - - for interferometer in self: - interferometer.plot_data(signal=signal, outdir=outdir, label=label) - - @property - def number_of_interferometers(self): - return len(self) - - @property - def duration(self): - return self[0].strain_data.duration - - @property - def start_time(self): - return self[0].strain_data.start_time - - @property - def sampling_frequency(self): - return self[0].strain_data.sampling_frequency - - @property - def frequency_array(self): - return self[0].strain_data.frequency_array - - def append(self, interferometer): - if isinstance(interferometer, InterferometerList): - super(InterferometerList, self).extend(interferometer) - else: - super(InterferometerList, self).append(interferometer) - self._check_interferometers() - - def extend(self, interferometers): - super(InterferometerList, self).extend(interferometers) - self._check_interferometers() - - def insert(self, index, interferometer): - super(InterferometerList, self).insert(index, interferometer) - self._check_interferometers() - - @property - def meta_data(self): - """ Dictionary of the per-interferometer meta_data """ - return {interferometer.name: interferometer.meta_data - for interferometer in self} - - @staticmethod - def _hdf5_filename_from_outdir_label(outdir, label): - return os.path.join(outdir, label + '.h5') - - def to_hdf5(self, outdir='outdir', label='ifo_list'): - """ Saves the object to a hdf5 file - - Parameters - ---------- - outdir: str, optional - Output directory name of the file - label: str, optional - Output file name, is 'ifo_list' if not given otherwise. A list of - the included interferometers will be appended. - """ - import deepdish - if sys.version_info[0] < 3: - raise NotImplementedError('Pickling of InterferometerList is not supported in Python 2.' - 'Use Python 3 instead.') - label = label + '_' + ''.join(ifo.name for ifo in self) - utils.check_directory_exists_and_if_not_mkdir(outdir) - deepdish.io.save(self._hdf5_filename_from_outdir_label(outdir, label), self) - - @classmethod - def from_hdf5(cls, filename=None): - """ Loads in an InterferometerList object from an hdf5 file - - Parameters - ---------- - filename: str - If given, try to load from this filename - - """ - import deepdish - if sys.version_info[0] < 3: - raise NotImplementedError('Pickling of InterferometerList is not supported in Python 2.' - 'Use Python 3 instead.') - res = deepdish.io.load(filename) - if res.__class__ == list: - res = cls(res) - if res.__class__ != cls: - raise TypeError('The loaded object is not a InterferometerList') - return res - - -class InterferometerStrainData(object): - """ Strain data for an interferometer """ - - def __init__(self, minimum_frequency=0, maximum_frequency=np.inf, - roll_off=0.2): - """ Initiate an InterferometerStrainData object - - The initialised object contains no data, this should be added using one - of the `set_from..` methods. - - Parameters - ---------- - minimum_frequency: float - Minimum frequency to analyse for detector. Default is 0. - maximum_frequency: float - Maximum frequency to analyse for detector. Default is infinity. - roll_off: float - The roll-off (in seconds) used in the Tukey window, default=0.2s. - This corresponds to alpha * duration / 2 for scipy tukey window. - - """ - self.minimum_frequency = minimum_frequency - self.maximum_frequency = maximum_frequency - self.roll_off = roll_off - self.window_factor = 1 - - self._times_and_frequencies = CoupledTimeAndFrequencySeries() - # self._set_time_and_frequency_array_parameters(None, None, None) - - self._frequency_domain_strain = None - self._frequency_array = None - self._time_domain_strain = None - self._time_array = None - self._channel = None - - def __eq__(self, other): - if self.minimum_frequency == other.minimum_frequency \ - and self.maximum_frequency == other.maximum_frequency \ - and self.roll_off == other.roll_off \ - and self.window_factor == other.window_factor \ - and self.sampling_frequency == other.sampling_frequency \ - and self.duration == other.duration \ - and self.start_time == other.start_time \ - and np.array_equal(self.time_array, other.time_array) \ - and np.array_equal(self.frequency_array, other.frequency_array) \ - and np.array_equal(self.frequency_domain_strain, other.frequency_domain_strain) \ - and np.array_equal(self.time_domain_strain, other.time_domain_strain): - return True - return False - - def time_within_data(self, time): - """ Check if time is within the data span - - Parameters - ---------- - time: float - The time to check - - Returns - ------- - bool: - A boolean stating whether the time is inside or outside the span - - """ - if time < self.start_time: - logger.debug("Time is before the start_time") - return False - elif time > self.start_time + self.duration: - logger.debug("Time is after the start_time + duration") - return False - else: - return True - - @property - def minimum_frequency(self): - return self.__minimum_frequency - - @minimum_frequency.setter - def minimum_frequency(self, minimum_frequency): - self.__minimum_frequency = minimum_frequency - - @property - def maximum_frequency(self): - """ Force the maximum frequency be less than the Nyquist frequency """ - if self.sampling_frequency is not None: - if 2 * self.__maximum_frequency > self.sampling_frequency: - self.__maximum_frequency = self.sampling_frequency / 2. - return self.__maximum_frequency - - @maximum_frequency.setter - def maximum_frequency(self, maximum_frequency): - self.__maximum_frequency = maximum_frequency - - @property - def frequency_mask(self): - """Masking array for limiting the frequency band. - - Returns - ------- - array_like: An array of boolean values - """ - try: - return self._frequency_mask - except AttributeError: - frequency_array = self._times_and_frequencies.frequency_array - mask = ((frequency_array >= self.minimum_frequency) & - (frequency_array <= self.maximum_frequency)) - self._frequency_mask = mask - return self._frequency_mask - - @property - def alpha(self): - return 2 * self.roll_off / self.duration - - def time_domain_window(self, roll_off=None, alpha=None): - """ - Window function to apply to time domain data before FFTing. - - This defines self.window_factor as the power loss due to the windowing. - See https://dcc.ligo.org/DocDB/0027/T040089/000/T040089-00.pdf - - Parameters - ---------- - roll_off: float - Rise time of window in seconds - alpha: float - Parameter to pass to tukey window, how much of segment falls - into windowed part - - Return - ------ - window: array - Window function over time array - """ - if roll_off is not None: - self.roll_off = roll_off - elif alpha is not None: - self.roll_off = alpha * self.duration / 2 - window = tukey(len(self._time_domain_strain), alpha=self.alpha) - self.window_factor = np.mean(window ** 2) - return window - - @property - def time_domain_strain(self): - """ The time domain strain, in units of strain """ - if self._time_domain_strain is not None: - return self._time_domain_strain - elif self._frequency_domain_strain is not None: - self._time_domain_strain = utils.infft( - self.frequency_domain_strain, self.sampling_frequency) - return self._time_domain_strain - - else: - raise ValueError("time domain strain data not yet set") - - @property - def frequency_domain_strain(self): - """ Returns the frequency domain strain - - This is the frequency domain strain normalised to units of - strain / Hz, obtained by a one-sided Fourier transform of the - time domain data, divided by the sampling frequency. - """ - if self._frequency_domain_strain is not None: - return self._frequency_domain_strain * self.frequency_mask - elif self._time_domain_strain is not None: - logger.info("Generating frequency domain strain from given time " - "domain strain.") - logger.info("Applying a tukey window with alpha={}, roll off={}".format( - self.alpha, self.roll_off)) - # self.low_pass_filter() - window = self.time_domain_window() - self._frequency_domain_strain, self.frequency_array = utils.nfft( - self._time_domain_strain * window, self.sampling_frequency) - return self._frequency_domain_strain * self.frequency_mask - else: - raise ValueError("frequency domain strain data not yet set") - - @frequency_domain_strain.setter - def frequency_domain_strain(self, frequency_domain_strain): - if not len(self.frequency_array) == len(frequency_domain_strain): - raise ValueError("The frequency_array and the set strain have different lengths") - self._frequency_domain_strain = frequency_domain_strain - - def to_gwpy_timeseries(self): - """ - Output the time series strain data as a :class:`gwpy.timeseries.TimeSeries`. - """ - - return gwpy.timeseries.TimeSeries(self.time_domain_strain, - sample_rate=self.sampling_frequency, - t0=self.start_time, - channel=self.channel) - - def to_pycbc_timeseries(self): - """ - Output the time series strain data as a :class:`pycbc.types.timeseries.TimeSeries`. - """ - - try: - import pycbc - except ImportError: - raise ImportError("Cannot output strain data as PyCBC TimeSeries") - - return pycbc.types.timeseries.TimeSeries(self.time_domain_strain, - delta_t=(1. / self.sampling_frequency), - epoch=lal.LIGOTimeGPS(self.start_time)) - - def to_lal_timeseries(self): - """ - Output the time series strain data as a LAL TimeSeries object. - """ - - laldata = lal.CreateREAL8TimeSeries("", - lal.LIGOTimeGPS(self.start_time), - 0., (1. / self.sampling_frequency), - lal.SecondUnit, - len(self.time_domain_strain)) - laldata.data.data[:] = self.time_domain_strain - - return laldata - - def to_gwpy_frequencyseries(self): - """ - Output the frequency series strain data as a :class:`gwpy.frequencyseries.FrequencySeries`. - """ - - return gwpy.frequencyseries.FrequencySeries(self.frequency_domain_strain, - frequencies=self.frequency_array, - epoch=self.start_time, - channel=self.channel) - - def to_pycbc_frequencyseries(self): - """ - Output the frequency series strain data as a :class:`pycbc.types.frequencyseries.FrequencySeries`. - """ - - try: - import pycbc - except ImportError: - raise ImportError("Cannot output strain data as PyCBC FrequencySeries") - - return pycbc.types.frequencyseries.FrequencySeries(self.frequency_domain_strain, - delta_f=(self.frequency_array[1] - self.frequency_array[0]), - epoch=lal.LIGOTimeGPS(self.start_time)) - - def to_lal_frequencyseries(self): - """ - Output the frequency series strain data as a LAL FrequencySeries object. - """ - - laldata = lal.CreateCOMPLEX16FrequencySeries("", - lal.LIGOTimeGPS(self.start_time), - self.frequency_array[0], - (self.frequency_array[1] - self.frequency_array[0]), - lal.SecondUnit, - len(self.frequency_domain_strain)) - laldata.data.data[:] = self.frequency_domain_strain - - return laldata - - def add_to_frequency_domain_strain(self, x): - """Deprecated""" - self._frequency_domain_strain += x - - def low_pass_filter(self, filter_freq=None): - """ Low pass filter the data """ - - if filter_freq is None: - logger.debug( - "Setting low pass filter_freq using given maximum frequency") - filter_freq = self.maximum_frequency - - if 2 * filter_freq >= self.sampling_frequency: - logger.info( - "Low pass filter frequency of {}Hz requested, this is equal" - " or greater than the Nyquist frequency so no filter applied" - .format(filter_freq)) - return - - logger.debug("Applying low pass filter with filter frequency {}".format(filter_freq)) - bp = gwpy.signal.filter_design.lowpass( - filter_freq, self.sampling_frequency) - strain = gwpy.timeseries.TimeSeries( - self.time_domain_strain, sample_rate=self.sampling_frequency) - strain = strain.filter(bp, filtfilt=True) - self._time_domain_strain = strain.value - - def create_power_spectral_density( - self, fft_length, overlap=0, name='unknown', outdir=None, - analysis_segment_start_time=None): - """ Use the time domain strain to generate a power spectral density - - This create a Tukey-windowed power spectral density and writes it to a - PSD file. - - Parameters - ---------- - fft_length: float - Duration of the analysis segment. - overlap: float - Number of seconds of overlap between FFTs. - name: str - The name of the detector, used in storing the PSD. Defaults to - "unknown". - outdir: str - The output directory to write the PSD file too. If not given, - the PSD will not be written to file. - analysis_segment_start_time: float - The start time of the analysis segment, if given, this data will - be removed before creating the PSD. - - Returns - ------- - frequency_array, psd : array_like - The frequencies and power spectral density array - - """ - - data = self.time_domain_strain - - if analysis_segment_start_time is not None: - analysis_segment_end_time = analysis_segment_start_time + fft_length - inside = (analysis_segment_start_time > self.time_array[0] + - analysis_segment_end_time < self.time_array[-1]) - if inside: - logger.info("Removing analysis segment data from the PSD data") - idxs = ( - (self.time_array < analysis_segment_start_time) + - (self.time_array > analysis_segment_end_time)) - data = data[idxs] - - # WARNING this line can cause issues if the data is non-contiguous - strain = gwpy.timeseries.TimeSeries(data=data, sample_rate=self.sampling_frequency) - psd_alpha = 2 * self.roll_off / fft_length - logger.info( - "Tukey window PSD data with alpha={}, roll off={}".format( - psd_alpha, self.roll_off)) - psd = strain.psd( - fftlength=fft_length, overlap=overlap, window=('tukey', psd_alpha)) - - if outdir: - psd_file = '{}/{}_PSD_{}_{}.txt'.format(outdir, name, self.start_time, self.duration) - with open('{}'.format(psd_file), 'w+') as opened_file: - for f, p in zip(psd.frequencies.value, psd.value): - opened_file.write('{} {}\n'.format(f, p)) - - return psd.frequencies.value, psd.value - - def _infer_time_domain_dependence( - self, start_time, sampling_frequency, duration, time_array): - """ Helper function to figure out if the time_array, or - sampling_frequency and duration where given - """ - self._infer_dependence(domain='time', array=time_array, duration=duration, - sampling_frequency=sampling_frequency, start_time=start_time) - - def _infer_frequency_domain_dependence( - self, start_time, sampling_frequency, duration, frequency_array): - """ Helper function to figure out if the frequency_array, or - sampling_frequency and duration where given - """ - - self._infer_dependence(domain='frequency', array=frequency_array, - duration=duration, sampling_frequency=sampling_frequency, start_time=start_time) - - def _infer_dependence(self, domain, array, duration, sampling_frequency, start_time): - if (sampling_frequency is not None) and (duration is not None): - if array is not None: - raise ValueError( - "You have given the sampling_frequency, duration, and " - "an array") - pass - elif array is not None: - if domain == 'time': - self.time_array = array - elif domain == 'frequency': - self.frequency_array = array - return - elif sampling_frequency is None or duration is None: - raise ValueError( - "You must provide both sampling_frequency and duration") - else: - raise ValueError( - "Insufficient information given to set arrays") - self._set_time_and_frequency_array_parameters(duration=duration, - sampling_frequency=sampling_frequency, - start_time=start_time) - - def set_from_time_domain_strain( - self, time_domain_strain, sampling_frequency=None, duration=None, - start_time=0, time_array=None): - """ Set the strain data from a time domain strain array - - This sets the time_domain_strain attribute, the frequency_domain_strain - is automatically calculated after a low-pass filter and Tukey window - is applied. - - Parameters - ---------- - time_domain_strain: array_like - An array of the time domain strain. - sampling_frequency: float - The sampling frequency (in Hz). - duration: float - The data duration (in s). - start_time: float - The GPS start-time of the data. - time_array: array_like - The array of times, if sampling_frequency and duration not - given. - - """ - self._infer_time_domain_dependence(start_time=start_time, - sampling_frequency=sampling_frequency, - duration=duration, - time_array=time_array) - - logger.debug('Setting data using provided time_domain_strain') - if np.shape(time_domain_strain) == np.shape(self.time_array): - self._time_domain_strain = time_domain_strain - self._frequency_domain_strain = None - else: - raise ValueError("Data times do not match time array") - - def set_from_gwpy_timeseries(self, time_series): - """ Set the strain data from a gwpy TimeSeries - - This sets the time_domain_strain attribute, the frequency_domain_strain - is automatically calculated after a low-pass filter and Tukey window - is applied. - - Parameters - ---------- - time_series: gwpy.timeseries.timeseries.TimeSeries - - """ - logger.debug('Setting data using provided gwpy TimeSeries object') - if type(time_series) != gwpy.timeseries.TimeSeries: - raise ValueError("Input time_series is not a gwpy TimeSeries") - self._set_time_and_frequency_array_parameters(duration=time_series.duration.value, - sampling_frequency=time_series.sample_rate.value, - start_time=time_series.epoch.value) - self._time_domain_strain = time_series.value - self._frequency_domain_strain = None - self._channel = time_series.channel - - @property - def channel(self): - return self._channel - - def set_from_open_data( - self, name, start_time, duration=4, outdir='outdir', cache=True, - **kwargs): - """ Set the strain data from open LOSC data - - This sets the time_domain_strain attribute, the frequency_domain_strain - is automatically calculated after a low-pass filter and Tukey window - is applied. - - Parameters - ---------- - name: str - Detector name, e.g., 'H1'. - start_time: float - Start GPS time of segment. - duration: float, optional - The total time (in seconds) to analyse. Defaults to 4s. - outdir: str - Directory where the psd files are saved - cache: bool, optional - Whether or not to store/use the acquired data. - **kwargs: - All keyword arguments are passed to - `gwpy.timeseries.TimeSeries.fetch_open_data()`. - - """ - - timeseries = gwutils.get_open_strain_data( - name, start_time, start_time + duration, outdir=outdir, cache=cache, - **kwargs) - - self.set_from_gwpy_timeseries(timeseries) - - def set_from_csv(self, filename): - """ Set the strain data from a csv file - - Parameters - ---------- - filename: str - The path to the file to read in - - """ - timeseries = gwpy.timeseries.TimeSeries.read(filename, format='csv') - self.set_from_gwpy_timeseries(timeseries) - - def set_from_frequency_domain_strain( - self, frequency_domain_strain, sampling_frequency=None, - duration=None, start_time=0, frequency_array=None): - """ Set the `frequency_domain_strain` from a numpy array - - Parameters - ---------- - frequency_domain_strain: array_like - The data to set. - sampling_frequency: float - The sampling frequency (in Hz). - duration: float - The data duration (in s). - start_time: float - The GPS start-time of the data. - frequency_array: array_like - The array of frequencies, if sampling_frequency and duration not - given. - - """ - - self._infer_frequency_domain_dependence(start_time=start_time, - sampling_frequency=sampling_frequency, - duration=duration, - frequency_array=frequency_array) - - logger.debug('Setting data using provided frequency_domain_strain') - if np.shape(frequency_domain_strain) == np.shape(self.frequency_array): - self._frequency_domain_strain = frequency_domain_strain - self.window_factor = 1 - else: - raise ValueError("Data frequencies do not match frequency_array") - - def set_from_power_spectral_density( - self, power_spectral_density, sampling_frequency, duration, - start_time=0): - """ Set the `frequency_domain_strain` by generating a noise realisation - - Parameters - ---------- - power_spectral_density: bilby.gw.detector.PowerSpectralDensity - A PowerSpectralDensity object used to generate the data - sampling_frequency: float - The sampling frequency (in Hz) - duration: float - The data duration (in s) - start_time: float - The GPS start-time of the data - - """ - - self._set_time_and_frequency_array_parameters(duration=duration, - sampling_frequency=sampling_frequency, - start_time=start_time) - - logger.debug( - 'Setting data using noise realization from provided' - 'power_spectal_density') - frequency_domain_strain, frequency_array = \ - power_spectral_density.get_noise_realisation( - self.sampling_frequency, self.duration) - - if np.array_equal(frequency_array, self.frequency_array): - self._frequency_domain_strain = frequency_domain_strain - else: - raise ValueError("Data frequencies do not match frequency_array") - - def set_from_zero_noise(self, sampling_frequency, duration, start_time=0): - """ Set the `frequency_domain_strain` to zero noise - - Parameters - ---------- - sampling_frequency: float - The sampling frequency (in Hz) - duration: float - The data duration (in s) - start_time: float - The GPS start-time of the data - - """ - - self._set_time_and_frequency_array_parameters(duration=duration, - sampling_frequency=sampling_frequency, - start_time=start_time) - - logger.debug('Setting zero noise data') - self._frequency_domain_strain = np.zeros_like(self.frequency_array, - dtype=np.complex) - - def set_from_frame_file( - self, frame_file, sampling_frequency, duration, start_time=0, - channel=None, buffer_time=1): - """ Set the `frequency_domain_strain` from a frame fiile - - Parameters - ---------- - frame_file: str - File from which to load data. - channel: str - Channel to read from frame. - sampling_frequency: float - The sampling frequency (in Hz) - duration: float - The data duration (in s) - start_time: float - The GPS start-time of the data - buffer_time: float - Read in data with `start_time-buffer_time` and - `start_time+duration+buffer_time` - - """ - - self._set_time_and_frequency_array_parameters(duration=duration, - sampling_frequency=sampling_frequency, - start_time=start_time) - - logger.info('Reading data from frame file {}'.format(frame_file)) - strain = gwutils.read_frame_file( - frame_file, start_time=start_time, end_time=start_time + duration, - buffer_time=buffer_time, channel=channel, - resample=sampling_frequency) - - self.set_from_gwpy_timeseries(strain) - - def _set_time_and_frequency_array_parameters(self, duration, sampling_frequency, start_time): - self._times_and_frequencies = CoupledTimeAndFrequencySeries(duration=duration, - sampling_frequency=sampling_frequency, - start_time=start_time) - - @property - def sampling_frequency(self): - return self._times_and_frequencies.sampling_frequency - - @sampling_frequency.setter - def sampling_frequency(self, sampling_frequency): - self._times_and_frequencies.sampling_frequency = sampling_frequency - - @property - def duration(self): - return self._times_and_frequencies.duration - - @duration.setter - def duration(self, duration): - self._times_and_frequencies.duration = duration - - @property - def start_time(self): - return self._times_and_frequencies.start_time - - @start_time.setter - def start_time(self, start_time): - self._times_and_frequencies.start_time = start_time - - @property - def frequency_array(self): - """ Frequencies of the data in Hz """ - return self._times_and_frequencies.frequency_array - - @frequency_array.setter - def frequency_array(self, frequency_array): - self._times_and_frequencies.frequency_array = frequency_array - - @property - def time_array(self): - """ Time of the data in seconds """ - return self._times_and_frequencies.time_array - - @time_array.setter - def time_array(self, time_array): - self._times_and_frequencies.time_array = time_array - - -class Interferometer(object): - """Class for the Interferometer """ - - def __init__(self, name, power_spectral_density, minimum_frequency, maximum_frequency, - length, latitude, longitude, elevation, xarm_azimuth, yarm_azimuth, - xarm_tilt=0., yarm_tilt=0., calibration_model=Recalibrate()): - """ - Instantiate an Interferometer object. - - Parameters - ---------- - name: str - Interferometer name, e.g., H1. - power_spectral_density: PowerSpectralDensity - Power spectral density determining the sensitivity of the detector. - minimum_frequency: float - Minimum frequency to analyse for detector. - maximum_frequency: float - Maximum frequency to analyse for detector. - length: float - Length of the interferometer in km. - latitude: float - Latitude North in degrees (South is negative). - longitude: float - Longitude East in degrees (West is negative). - elevation: float - Height above surface in metres. - xarm_azimuth: float - Orientation of the x arm in degrees North of East. - yarm_azimuth: float - Orientation of the y arm in degrees North of East. - xarm_tilt: float, optional - Tilt of the x arm in radians above the horizontal defined by - ellipsoid earth model in LIGO-T980044-08. - yarm_tilt: float, optional - Tilt of the y arm in radians above the horizontal. - calibration_model: Recalibration - Calibration model, this applies the calibration correction to the - template, the default model applies no correction. - """ - self.__x_updated = False - self.__y_updated = False - self.__vertex_updated = False - self.__detector_tensor_updated = False - - self.name = name - self.length = length - self.latitude = latitude - self.longitude = longitude - self.elevation = elevation - self.xarm_azimuth = xarm_azimuth - self.yarm_azimuth = yarm_azimuth - self.xarm_tilt = xarm_tilt - self.yarm_tilt = yarm_tilt - self.power_spectral_density = power_spectral_density - self.calibration_model = calibration_model - self._strain_data = InterferometerStrainData( - minimum_frequency=minimum_frequency, - maximum_frequency=maximum_frequency) - self.meta_data = dict() - - def __eq__(self, other): - if self.name == other.name and \ - self.length == other.length and \ - self.latitude == other.latitude and \ - self.longitude == other.longitude and \ - self.elevation == other.elevation and \ - self.xarm_azimuth == other.xarm_azimuth and \ - self.xarm_tilt == other.xarm_tilt and \ - self.yarm_azimuth == other.yarm_azimuth and \ - self.yarm_tilt == other.yarm_tilt and \ - self.power_spectral_density.__eq__(other.power_spectral_density) and \ - self.calibration_model == other.calibration_model and \ - self.strain_data == other.strain_data: - return True - return False - - def __repr__(self): - return self.__class__.__name__ + '(name=\'{}\', power_spectral_density={}, minimum_frequency={}, ' \ - 'maximum_frequency={}, length={}, latitude={}, longitude={}, elevation={}, ' \ - 'xarm_azimuth={}, yarm_azimuth={}, xarm_tilt={}, yarm_tilt={})' \ - .format(self.name, self.power_spectral_density, float(self.minimum_frequency), - float(self.maximum_frequency), float(self.length), float(self.latitude), float(self.longitude), - float(self.elevation), float(self.xarm_azimuth), float(self.yarm_azimuth), float(self.xarm_tilt), - float(self.yarm_tilt)) - - @property - def minimum_frequency(self): - return self.strain_data.minimum_frequency - - @minimum_frequency.setter - def minimum_frequency(self, minimum_frequency): - self._strain_data.minimum_frequency = minimum_frequency - - @property - def maximum_frequency(self): - return self.strain_data.maximum_frequency - - @maximum_frequency.setter - def maximum_frequency(self, maximum_frequency): - self._strain_data.maximum_frequency = maximum_frequency - - @property - def strain_data(self): - """ A bilby.gw.detector.InterferometerStrainData instance """ - return self._strain_data - - @strain_data.setter - def strain_data(self, strain_data): - """ Set the strain_data - - This sets the Interferometer.strain_data equal to the provided - strain_data. This will override the minimum_frequency and - maximum_frequency of the provided strain_data object with those of - the Interferometer object. - """ - strain_data.minimum_frequency = self.minimum_frequency - strain_data.maximum_frequency = self.maximum_frequency - - self._strain_data = strain_data - - def set_strain_data_from_frequency_domain_strain( - self, frequency_domain_strain, sampling_frequency=None, - duration=None, start_time=0, frequency_array=None): - """ Set the `Interferometer.strain_data` from a numpy array - - Parameters - ---------- - frequency_domain_strain: array_like - The data to set. - sampling_frequency: float - The sampling frequency (in Hz). - duration: float - The data duration (in s). - start_time: float - The GPS start-time of the data. - frequency_array: array_like - The array of frequencies, if sampling_frequency and duration not - given. - - """ - self.strain_data.set_from_frequency_domain_strain( - frequency_domain_strain=frequency_domain_strain, - sampling_frequency=sampling_frequency, duration=duration, - start_time=start_time, frequency_array=frequency_array) - - def set_strain_data_from_power_spectral_density( - self, sampling_frequency, duration, start_time=0): - """ Set the `Interferometer.strain_data` from a power spectal density - - This uses the `interferometer.power_spectral_density` object to set - the `strain_data` to a noise realization. See - `bilby.gw.detector.InterferometerStrainData` for further information. - - Parameters - ---------- - sampling_frequency: float - The sampling frequency (in Hz) - duration: float - The data duration (in s) - start_time: float - The GPS start-time of the data - - """ - self.strain_data.set_from_power_spectral_density( - self.power_spectral_density, sampling_frequency=sampling_frequency, - duration=duration, start_time=start_time) - - def set_strain_data_from_frame_file( - self, frame_file, sampling_frequency, duration, start_time=0, - channel=None, buffer_time=1): - """ Set the `Interferometer.strain_data` from a frame file - - Parameters - ---------- - frame_file: str - File from which to load data. - channel: str - Channel to read from frame. - sampling_frequency: float - The sampling frequency (in Hz) - duration: float - The data duration (in s) - start_time: float - The GPS start-time of the data - buffer_time: float - Read in data with `start_time-buffer_time` and - `start_time+duration+buffer_time` - - """ - self.strain_data.set_from_frame_file( - frame_file=frame_file, sampling_frequency=sampling_frequency, - duration=duration, start_time=start_time, - channel=channel, buffer_time=buffer_time) - - def set_strain_data_from_csv(self, filename): - """ Set the `Interferometer.strain_data` from a csv file - - Parameters - ---------- - filename: str - The path to the file to read in - - """ - self.strain_data.set_from_csv(filename) - - def set_strain_data_from_zero_noise( - self, sampling_frequency, duration, start_time=0): - """ Set the `Interferometer.strain_data` to zero noise - - Parameters - ---------- - sampling_frequency: float - The sampling frequency (in Hz) - duration: float - The data duration (in s) - start_time: float - The GPS start-time of the data - - """ - - self.strain_data.set_from_zero_noise( - sampling_frequency=sampling_frequency, duration=duration, - start_time=start_time) - - @property - def latitude(self): - """ Saves latitude in rad internally. Updates related quantities if set to a different value. - - Returns - ------- - float: The latitude position of the detector in degree - """ - return self.__latitude * 180 / np.pi - - @latitude.setter - def latitude(self, latitude): - self.__latitude = latitude * np.pi / 180 - self.__x_updated = False - self.__y_updated = False - self.__vertex_updated = False - - @property - def longitude(self): - """ Saves longitude in rad internally. Updates related quantities if set to a different value. - - Returns - ------- - float: The longitude position of the detector in degree - """ - return self.__longitude * 180 / np.pi - - @longitude.setter - def longitude(self, longitude): - self.__longitude = longitude * np.pi / 180 - self.__x_updated = False - self.__y_updated = False - self.__vertex_updated = False - - @property - def elevation(self): - """ Updates related quantities if set to a different values. - - Returns - ------- - float: The height about the surface in meters - """ - return self.__elevation - - @elevation.setter - def elevation(self, elevation): - self.__elevation = elevation - self.__vertex_updated = False - - @property - def xarm_azimuth(self): - """ Saves the x-arm azimuth in rad internally. Updates related quantities if set to a different values. - - Returns - ------- - float: The x-arm azimuth in degrees. - - """ - return self.__xarm_azimuth * 180 / np.pi - - @xarm_azimuth.setter - def xarm_azimuth(self, xarm_azimuth): - self.__xarm_azimuth = xarm_azimuth * np.pi / 180 - self.__x_updated = False - - @property - def yarm_azimuth(self): - """ Saves the y-arm azimuth in rad internally. Updates related quantities if set to a different values. - - Returns - ------- - float: The y-arm azimuth in degrees. - - """ - return self.__yarm_azimuth * 180 / np.pi - - @yarm_azimuth.setter - def yarm_azimuth(self, yarm_azimuth): - self.__yarm_azimuth = yarm_azimuth * np.pi / 180 - self.__y_updated = False - - @property - def xarm_tilt(self): - """ Updates related quantities if set to a different values. - - Returns - ------- - float: The x-arm tilt in radians. - - """ - return self.__xarm_tilt - - @xarm_tilt.setter - def xarm_tilt(self, xarm_tilt): - self.__xarm_tilt = xarm_tilt - self.__x_updated = False - - @property - def yarm_tilt(self): - """ Updates related quantities if set to a different values. - - Returns - ------- - float: The y-arm tilt in radians. - - """ - return self.__yarm_tilt - - @yarm_tilt.setter - def yarm_tilt(self, yarm_tilt): - self.__yarm_tilt = yarm_tilt - self.__y_updated = False - - @property - def vertex(self): - """ Position of the IFO vertex in geocentric coordinates in meters. - - Is automatically updated if related quantities are modified. - - Returns - ------- - array_like: A 3D array representation of the vertex - """ - if not self.__vertex_updated: - self.__vertex = gwutils.get_vertex_position_geocentric(self.__latitude, self.__longitude, - self.elevation) - self.__vertex_updated = True - return self.__vertex - - @property - def x(self): - """ A unit vector along the x-arm - - Is automatically updated if related quantities are modified. - - Returns - ------- - array_like: A 3D array representation of a unit vector along the x-arm - - """ - if not self.__x_updated: - self.__x = self.unit_vector_along_arm('x') - self.__x_updated = True - self.__detector_tensor_updated = False - return self.__x - - @property - def y(self): - """ A unit vector along the y-arm - - Is automatically updated if related quantities are modified. - - Returns - ------- - array_like: A 3D array representation of a unit vector along the y-arm - - """ - if not self.__y_updated: - self.__y = self.unit_vector_along_arm('y') - self.__y_updated = True - self.__detector_tensor_updated = False - return self.__y - - @property - def detector_tensor(self): - """ - Calculate the detector tensor from the unit vectors along each arm of the detector. - - See Eq. B6 of arXiv:gr-qc/0008066 - - Is automatically updated if related quantities are modified. - - Returns - ------- - array_like: A 3x3 array representation of the detector tensor - - """ - if not self.__x_updated or not self.__y_updated: - _, _ = self.x, self.y # noqa - if not self.__detector_tensor_updated: - self.__detector_tensor = 0.5 * (np.einsum('i,j->ij', self.x, self.x) - np.einsum('i,j->ij', self.y, self.y)) - self.__detector_tensor_updated = True - return self.__detector_tensor - - def antenna_response(self, ra, dec, time, psi, mode): - """ - Calculate the antenna response function for a given sky location - - See Nishizawa et al. (2009) arXiv:0903.0528 for definitions of the polarisation tensors. - [u, v, w] represent the Earth-frame - [m, n, omega] represent the wave-frame - Note: there is a typo in the definition of the wave-frame in Nishizawa et al. - - Parameters - ------- - ra: float - right ascension in radians - dec: float - declination in radians - time: float - geocentric GPS time - psi: float - binary polarisation angle counter-clockwise about the direction of propagation - mode: str - polarisation mode (e.g. 'plus', 'cross') - - Returns - ------- - array_like: A 3x3 array representation of the antenna response for the specified mode - - """ - polarization_tensor = gwutils.get_polarization_tensor(ra, dec, time, psi, mode) - return np.einsum('ij,ij->', self.detector_tensor, polarization_tensor) - - def get_detector_response(self, waveform_polarizations, parameters): - """ Get the detector response for a particular waveform - - Parameters - ------- - waveform_polarizations: dict - polarizations of the waveform - parameters: dict - parameters describing position and time of arrival of the signal - - Returns - ------- - array_like: A 3x3 array representation of the detector response (signal observed in the interferometer) - """ - signal = {} - for mode in waveform_polarizations.keys(): - det_response = self.antenna_response( - parameters['ra'], - parameters['dec'], - parameters['geocent_time'], - parameters['psi'], mode) - - signal[mode] = waveform_polarizations[mode] * det_response - signal_ifo = sum(signal.values()) - - signal_ifo *= self.strain_data.frequency_mask - - time_shift = self.time_delay_from_geocenter( - parameters['ra'], parameters['dec'], parameters['geocent_time']) - dt = parameters['geocent_time'] + time_shift - self.strain_data.start_time - - signal_ifo = signal_ifo * np.exp( - -1j * 2 * np.pi * dt * self.frequency_array) - - signal_ifo *= self.calibration_model.get_calibration_factor( - self.frequency_array, prefix='recalib_{}_'.format(self.name), **parameters) - - return signal_ifo - - def inject_signal(self, parameters=None, injection_polarizations=None, - waveform_generator=None): - """ Inject a signal into noise - - Parameters - ---------- - parameters: dict - Parameters of the injection. - 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. - - Note - ------- - if your signal takes a substantial amount of time to generate, or - you experience buggy behaviour. It is preferable to provide the - injection_polarizations directly. - - Returns - ------- - injection_polarizations: dict - - """ - - if injection_polarizations is None: - if waveform_generator is not None: - injection_polarizations = \ - waveform_generator.frequency_domain_strain(parameters) - else: - raise ValueError( - "inject_signal needs one of waveform_generator or " - "injection_polarizations.") - - if injection_polarizations is None: - raise ValueError( - 'Trying to inject signal which is None. The most likely cause' - ' is that waveform_generator.frequency_domain_strain returned' - ' None. This can be caused if, e.g., mass_2 > mass_1.') - - if not self.strain_data.time_within_data(parameters['geocent_time']): - logger.warning( - 'Injecting signal outside segment, start_time={}, merger time={}.' - .format(self.strain_data.start_time, parameters['geocent_time'])) - - signal_ifo = self.get_detector_response(injection_polarizations, parameters) - if np.shape(self.frequency_domain_strain).__eq__(np.shape(signal_ifo)): - self.strain_data.frequency_domain_strain = \ - signal_ifo + self.strain_data.frequency_domain_strain - else: - logger.info('Injecting into zero noise.') - self.set_strain_data_from_frequency_domain_strain( - signal_ifo, - sampling_frequency=self.strain_data.sampling_frequency, - duration=self.strain_data.duration, - start_time=self.strain_data.start_time) - - self.meta_data['optimal_SNR'] = ( - np.sqrt(self.optimal_snr_squared(signal=signal_ifo)).real) - self.meta_data['matched_filter_SNR'] = ( - self.matched_filter_snr(signal=signal_ifo)) - self.meta_data['parameters'] = parameters - - logger.info("Injected signal in {}:".format(self.name)) - logger.info(" optimal SNR = {:.2f}".format(self.meta_data['optimal_SNR'])) - logger.info(" matched filter SNR = {:.2f}".format(self.meta_data['matched_filter_SNR'])) - for key in parameters: - logger.info(' {} = {}'.format(key, parameters[key])) - - return injection_polarizations - - def unit_vector_along_arm(self, arm): - """ - Calculate the unit vector pointing along the specified arm in cartesian Earth-based coordinates. - - See Eqs. B14-B17 in arXiv:gr-qc/0008066 - - Parameters - ------- - arm: str - 'x' or 'y' (arm of the detector) - - Returns - ------- - array_like: 3D unit vector along arm in cartesian Earth-based coordinates - - Raises - ------- - ValueError: If arm is neither 'x' nor 'y' - - """ - if arm == 'x': - return self.__calculate_arm(self.__xarm_tilt, self.__xarm_azimuth) - elif arm == 'y': - return self.__calculate_arm(self.__yarm_tilt, self.__yarm_azimuth) - else: - raise ValueError("Arm must either be 'x' or 'y'.") - - def __calculate_arm(self, arm_tilt, arm_azimuth): - e_long = np.array([-np.sin(self.__longitude), np.cos(self.__longitude), 0]) - e_lat = np.array([-np.sin(self.__latitude) * np.cos(self.__longitude), - -np.sin(self.__latitude) * np.sin(self.__longitude), np.cos(self.__latitude)]) - e_h = np.array([np.cos(self.__latitude) * np.cos(self.__longitude), - np.cos(self.__latitude) * np.sin(self.__longitude), np.sin(self.__latitude)]) - - return (np.cos(arm_tilt) * np.cos(arm_azimuth) * e_long + - np.cos(arm_tilt) * np.sin(arm_azimuth) * e_lat + - np.sin(arm_tilt) * e_h) - - @property - def amplitude_spectral_density_array(self): - """ Returns the amplitude spectral density (ASD) given we know a power spectral denstiy (PSD) - - Returns - ------- - array_like: An array representation of the ASD - - """ - return self.power_spectral_density_array ** 0.5 - - @property - def power_spectral_density_array(self): - """ Returns the power spectral density (PSD) - - This accounts for whether the data in the interferometer has been windowed. - - Returns - ------- - array_like: An array representation of the PSD - - """ - return (self.power_spectral_density.power_spectral_density_interpolated(self.frequency_array) * - self.strain_data.window_factor) - - @property - def frequency_array(self): - return self.strain_data.frequency_array - - @property - def frequency_mask(self): - return self.strain_data.frequency_mask - - @property - def frequency_domain_strain(self): - """ The frequency domain strain in units of strain / Hz """ - return self.strain_data.frequency_domain_strain - - @property - def time_domain_strain(self): - """ The time domain strain in units of s """ - return self.strain_data.time_domain_strain - - @property - def time_array(self): - return self.strain_data.time_array - - def time_delay_from_geocenter(self, ra, dec, time): - """ - Calculate the time delay from the geocenter for the interferometer. - - Use the time delay function from utils. - - Parameters - ------- - ra: float - right ascension of source in radians - dec: float - declination of source in radians - time: float - GPS time - - Returns - ------- - float: The time delay from geocenter in seconds - """ - return gwutils.time_delay_geocentric(self.vertex, np.array([0, 0, 0]), ra, dec, time) - - def vertex_position_geocentric(self): - """ - Calculate the position of the IFO vertex in geocentric coordinates in meters. - - Based on arXiv:gr-qc/0008066 Eqs. B11-B13 except for the typo in the definition of the local radius. - See Section 2.1 of LIGO-T980044-10 for the correct expression - - Returns - ------- - array_like: A 3D array representation of the vertex - """ - return gwutils.get_vertex_position_geocentric(self.__latitude, self.__longitude, self.__elevation) - - def optimal_snr_squared(self, signal): - """ - - Parameters - ---------- - signal: array_like - Array containing the signal - - Returns - ------- - float: The optimal signal to noise ratio possible squared - """ - return gwutils.optimal_snr_squared( - signal=signal[self.frequency_mask], - power_spectral_density=self.power_spectral_density_array[self.frequency_mask], - duration=self.strain_data.duration) - - def inner_product(self, signal): - """ - - Parameters - ---------- - signal: array_like - Array containing the signal - - Returns - ------- - float: The optimal signal to noise ratio possible squared - """ - return gwutils.noise_weighted_inner_product( - aa=signal[self.frequency_mask], - bb=self.frequency_domain_strain[self.frequency_mask], - power_spectral_density=self.power_spectral_density_array[self.frequency_mask], - duration=self.strain_data.duration) - - def matched_filter_snr(self, signal): - """ - - Parameters - ---------- - signal: array_like - Array containing the signal - - Returns - ------- - float: The matched filter signal to noise ratio squared - - """ - return gwutils.matched_filter_snr( - signal=signal[self.frequency_mask], - frequency_domain_strain=self.frequency_domain_strain[self.frequency_mask], - power_spectral_density=self.power_spectral_density_array[self.frequency_mask], - duration=self.strain_data.duration) - - @property - def whitened_frequency_domain_strain(self): - """ Calculates the whitened data by dividing data by the amplitude spectral density - - Returns - ------- - array_like: The whitened data - """ - return self.strain_data.frequency_domain_strain / self.amplitude_spectral_density_array - - def save_data(self, outdir, label=None): - """ Creates a save file for the data in plain text format - - Parameters - ---------- - outdir: str - The output directory in which the data is supposed to be saved - label: str - The name of the output files - """ - - if label is None: - filename_psd = '{}/{}_psd.dat'.format(outdir, self.name) - filename_data = '{}/{}_frequency_domain_data.dat'.format(outdir, self.name) - else: - filename_psd = '{}/{}_{}_psd.dat'.format(outdir, self.name, label) - filename_data = '{}/{}_{}_frequency_domain_data.dat'.format(outdir, self.name, label) - np.savetxt(filename_data, - np.array( - [self.frequency_array, - self.frequency_domain_strain.real, - self.frequency_domain_strain.imag]).T, - header='f real_h(f) imag_h(f)') - np.savetxt(filename_psd, - np.array( - [self.frequency_array, - self.amplitude_spectral_density_array]).T, - header='f h(f)') - - def plot_data(self, signal=None, outdir='.', label=None): - if utils.command_line_args.test: - return - - fig, ax = plt.subplots() - df = self.frequency_array[1] - self.frequency_array[0] - asd = gwutils.asd_from_freq_series( - freq_data=self.frequency_domain_strain, df=df) - - ax.loglog(self.frequency_array[self.frequency_mask], - asd[self.frequency_mask], - color='C0', label=self.name) - ax.loglog(self.frequency_array[self.frequency_mask], - self.amplitude_spectral_density_array[self.frequency_mask], - color='C1', lw=1.0, label=self.name + ' ASD') - if signal is not None: - signal_asd = gwutils.asd_from_freq_series( - freq_data=signal, df=df) - - ax.loglog(self.frequency_array[self.frequency_mask], - signal_asd[self.frequency_mask], - color='C2', - label='Signal') - ax.grid(True) - ax.set_ylabel(r'Strain [strain/$\sqrt{\rm Hz}$]') - ax.set_xlabel(r'Frequency [Hz]') - ax.legend(loc='best') - fig.tight_layout() - if label is None: - fig.savefig( - '{}/{}_frequency_domain_data.png'.format(outdir, self.name)) - else: - fig.savefig( - '{}/{}_{}_frequency_domain_data.png'.format( - outdir, self.name, label)) - plt.close(fig) - - def plot_time_domain_data( - self, outdir='.', label=None, bandpass_frequencies=(50, 250), - notches=None, start_end=None, t0=None): - """ Plots the strain data in the time domain - - Parameters - ---------- - outdir, label: str - Used in setting the saved filename. - bandpass: tuple, optional - A tuple of the (low, high) frequencies to use when bandpassing the - data, if None no bandpass is applied. - notches: list, optional - A list of frequencies specifying any lines to notch - start_end: tuple - A tuple of the (start, end) range of GPS times to plot - t0: float - If given, the reference time to subtract from the time series before - plotting. - - """ - - # We use the gwpy timeseries to perform bandpass and notching - if notches is None: - notches = list() - timeseries = gwpy.timeseries.TimeSeries( - data=self.time_domain_strain, times=self.time_array) - zpks = [] - if bandpass_frequencies is not None: - zpks.append(gwpy.signal.filter_design.bandpass( - bandpass_frequencies[0], bandpass_frequencies[1], - self.strain_data.sampling_frequency)) - if notches is not None: - for line in notches: - zpks.append(gwpy.signal.filter_design.notch( - line, self.strain_data.sampling_frequency)) - if len(zpks) > 0: - zpk = gwpy.signal.filter_design.concatenate_zpks(*zpks) - strain = timeseries.filter(zpk, filtfilt=True) - else: - strain = timeseries - - fig, ax = plt.subplots() - - if t0: - x = self.time_array - t0 - xlabel = 'GPS time [s] - {}'.format(t0) - else: - x = self.time_array - xlabel = 'GPS time [s]' - - ax.plot(x, strain) - ax.set_xlabel(xlabel) - ax.set_ylabel('Strain') - - if start_end is not None: - ax.set_xlim(*start_end) - - fig.tight_layout() - - if label is None: - fig.savefig( - '{}/{}_time_domain_data.png'.format(outdir, self.name)) - else: - fig.savefig( - '{}/{}_{}_time_domain_data.png'.format(outdir, self.name, label)) - plt.close(fig) - - @staticmethod - def _hdf5_filename_from_outdir_label(outdir, label): - return os.path.join(outdir, label + '.h5') - - def to_hdf5(self, outdir='outdir', label=None): - """ Save the object to a hdf5 file - - Attributes - ---------- - outdir: str, optional - Output directory name of the file, defaults to 'outdir'. - label: str, optional - Output file name, is self.name if not given otherwise. - """ - import deepdish - if sys.version_info[0] < 3: - raise NotImplementedError('Pickling of Interferometer is not supported in Python 2.' - 'Use Python 3 instead.') - if label is None: - label = self.name - utils.check_directory_exists_and_if_not_mkdir('outdir') - filename = self._hdf5_filename_from_outdir_label(outdir, label) - deepdish.io.save(filename, self) - - @classmethod - def from_hdf5(cls, filename=None): - """ Loads in an Interferometer object from an hdf5 file - - Parameters - ---------- - filename: str - If given, try to load from this filename - - """ - import deepdish - if sys.version_info[0] < 3: - raise NotImplementedError('Pickling of Interferometer is not supported in Python 2.' - 'Use Python 3 instead.') - - res = deepdish.io.load(filename) - if res.__class__ != cls: - raise TypeError('The loaded object is not an Interferometer') - return res - - -class TriangularInterferometer(InterferometerList): - - def __init__(self, name, power_spectral_density, minimum_frequency, maximum_frequency, - length, latitude, longitude, elevation, xarm_azimuth, yarm_azimuth, - xarm_tilt=0., yarm_tilt=0.): - InterferometerList.__init__(self, []) - self.name = name - # for attr in ['power_spectral_density', 'minimum_frequency', 'maximum_frequency']: - if isinstance(power_spectral_density, PowerSpectralDensity): - power_spectral_density = [power_spectral_density] * 3 - if isinstance(minimum_frequency, float) or isinstance(minimum_frequency, int): - minimum_frequency = [minimum_frequency] * 3 - if isinstance(maximum_frequency, float) or isinstance(maximum_frequency, int): - maximum_frequency = [maximum_frequency] * 3 - - for ii in range(3): - self.append(Interferometer( - '{}{}'.format(name, ii + 1), power_spectral_density[ii], minimum_frequency[ii], maximum_frequency[ii], - length, latitude, longitude, elevation, xarm_azimuth, yarm_azimuth, xarm_tilt, yarm_tilt)) - - xarm_azimuth += 240 - yarm_azimuth += 240 - - 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) - - -class PowerSpectralDensity(object): - - def __init__(self, frequency_array=None, psd_array=None, asd_array=None, - psd_file=None, asd_file=None): - """ - Instantiate a new PowerSpectralDensity object. - - Example - ------- - Using the `from` method directly (here `psd_file` is a string - containing the path to the file to load): - >>> power_spectral_density = PowerSpectralDensity.from_power_spectral_density_file(psd_file) - - Alternatively (and equivalently) setting the psd_file directly: - >>> power_spectral_density = PowerSpectralDensity(psd_file=psd_file) - - Attributes - ---------- - asd_array: array_like - Array representation of the ASD - asd_file: str - Name of the ASD file - frequency_array: array_like - Array containing the frequencies of the ASD/PSD values - psd_array: array_like - Array representation of the PSD - psd_file: str - Name of the PSD file - power_spectral_density_interpolated: scipy.interpolated.interp1d - Interpolated function of the PSD - - """ - self.frequency_array = np.array(frequency_array) - if psd_array is not None: - self.psd_array = psd_array - if asd_array is not None: - self.asd_array = asd_array - self.psd_file = psd_file - self.asd_file = asd_file - - def __eq__(self, other): - if self.psd_file == other.psd_file \ - and self.asd_file == other.asd_file \ - and np.array_equal(self.frequency_array, other.frequency_array) \ - and np.array_equal(self.psd_array, other.psd_array) \ - and np.array_equal(self.asd_array, other.asd_array): - return True - return False - - def __repr__(self): - if self.asd_file is not None or self.psd_file is not None: - return self.__class__.__name__ + '(psd_file=\'{}\', asd_file=\'{}\')' \ - .format(self.psd_file, self.asd_file) - else: - return self.__class__.__name__ + '(frequency_array={}, psd_array={}, asd_array={})' \ - .format(self.frequency_array, self.psd_array, self.asd_array) - - @staticmethod - def from_amplitude_spectral_density_file(asd_file): - """ Set the amplitude spectral density from a given file - - Parameters - ---------- - asd_file: str - File containing amplitude spectral density, format 'f h_f' - - """ - return PowerSpectralDensity(asd_file=asd_file) - - @staticmethod - def from_power_spectral_density_file(psd_file): - """ Set the power spectral density from a given file - - Parameters - ---------- - psd_file: str, optional - File containing power spectral density, format 'f h_f' - - """ - return PowerSpectralDensity(psd_file=psd_file) - - @staticmethod - def from_frame_file(frame_file, psd_start_time, psd_duration, - fft_length=4, sampling_frequency=4096, roll_off=0.2, - overlap=0, channel=None, name=None, outdir=None, - analysis_segment_start_time=None): - """ Generate power spectral density from a frame file - - Parameters - ---------- - frame_file: str, optional - Frame file to read data from. - psd_start_time: float - Beginning of segment to analyse. - psd_duration: float, optional - Duration of data (in seconds) to generate PSD from. - fft_length: float, optional - Number of seconds in a single fft. - sampling_frequency: float, optional - Sampling frequency for time series. - This is twice the maximum frequency. - roll_off: float, optional - Rise time in seconds of tukey window. - overlap: float, - Number of seconds of overlap between FFTs. - channel: str, optional - Name of channel to use to generate PSD. - name, outdir: str, optional - Name (and outdir) of the detector for which a PSD is to be - generated. - analysis_segment_start_time: float, optional - The start time of the analysis segment, if given, this data will - be removed before creating the PSD. - - """ - strain = InterferometerStrainData(roll_off=roll_off) - strain.set_from_frame_file( - frame_file, start_time=psd_start_time, duration=psd_duration, - channel=channel, sampling_frequency=sampling_frequency) - frequency_array, psd_array = strain.create_power_spectral_density( - fft_length=fft_length, name=name, outdir=outdir, overlap=overlap, - analysis_segment_start_time=analysis_segment_start_time) - return PowerSpectralDensity(frequency_array=frequency_array, psd_array=psd_array) - - @staticmethod - def from_amplitude_spectral_density_array(frequency_array, asd_array): - return PowerSpectralDensity(frequency_array=frequency_array, asd_array=asd_array) - - @staticmethod - def from_power_spectral_density_array(frequency_array, psd_array): - return PowerSpectralDensity(frequency_array=frequency_array, psd_array=psd_array) - - @staticmethod - def from_aligo(): - logger.info("No power spectral density provided, using aLIGO," - "zero detuning, high power.") - return PowerSpectralDensity.from_power_spectral_density_file(psd_file='aLIGO_ZERO_DET_high_P_psd.txt') - - @property - def psd_array(self): - return self.__psd_array - - @psd_array.setter - def psd_array(self, psd_array): - self.__check_frequency_array_matches_density_array(psd_array) - self.__psd_array = np.array(psd_array) - self.__asd_array = psd_array ** 0.5 - self.__interpolate_power_spectral_density() - - @property - def asd_array(self): - return self.__asd_array - - @asd_array.setter - def asd_array(self, asd_array): - self.__check_frequency_array_matches_density_array(asd_array) - self.__asd_array = np.array(asd_array) - self.__psd_array = asd_array ** 2 - self.__interpolate_power_spectral_density() - - def __check_frequency_array_matches_density_array(self, density_array): - if len(self.frequency_array) != len(density_array): - raise ValueError('Provided spectral density does not match frequency array. Not updating.\n' - 'Length spectral density {}\n Length frequency array {}\n' - .format(density_array, self.frequency_array)) - - def __interpolate_power_spectral_density(self): - """Interpolate the loaded power spectral density so it can be resampled - for arbitrary frequency arrays. - """ - self.__power_spectral_density_interpolated = interp1d(self.frequency_array, - self.psd_array, - bounds_error=False, - fill_value=np.inf) - - @property - def power_spectral_density_interpolated(self): - return self.__power_spectral_density_interpolated - - @property - def asd_file(self): - return self._asd_file - - @asd_file.setter - def asd_file(self, asd_file): - asd_file = self.__validate_file_name(file=asd_file) - self._asd_file = asd_file - if asd_file is not None: - self.__import_amplitude_spectral_density() - self.__check_file_was_asd_file() - - def __check_file_was_asd_file(self): - if min(self.asd_array) < 1e-30: - logger.warning("You specified an amplitude spectral density file.") - logger.warning("{} WARNING {}".format("*" * 30, "*" * 30)) - logger.warning("The minimum of the provided curve is {:.2e}.".format(min(self.asd_array))) - logger.warning("You may have intended to provide this as a power spectral density.") - - @property - def psd_file(self): - return self._psd_file - - @psd_file.setter - def psd_file(self, psd_file): - psd_file = self.__validate_file_name(file=psd_file) - self._psd_file = psd_file - if psd_file is not None: - self.__import_power_spectral_density() - self.__check_file_was_psd_file() - - def __check_file_was_psd_file(self): - if min(self.psd_array) > 1e-30: - logger.warning("You specified a power spectral density file.") - logger.warning("{} WARNING {}".format("*" * 30, "*" * 30)) - logger.warning("The minimum of the provided curve is {:.2e}.".format(min(self.psd_array))) - logger.warning("You may have intended to provide this as an amplitude spectral density.") - - @staticmethod - def __validate_file_name(file): - """ - Test if the file contains a path (i.e., contains '/'). - If not assume the file is in the default directory. - """ - if file is not None and '/' not in file: - file = os.path.join(os.path.dirname(__file__), 'noise_curves', file) - return file - - def __import_amplitude_spectral_density(self): - """ Automagically load an amplitude spectral density curve """ - self.frequency_array, self.asd_array = np.genfromtxt(self.asd_file).T - - def __import_power_spectral_density(self): - """ Automagically load a power spectral density curve """ - self.frequency_array, self.psd_array = np.genfromtxt(self.psd_file).T - - def get_noise_realisation(self, sampling_frequency, duration): - """ - Generate frequency Gaussian noise scaled to the power spectral density. - - Parameters - ------- - sampling_frequency: float - sampling frequency of noise - duration: float - duration of noise - - Returns - ------- - array_like: frequency domain strain of this noise realisation - array_like: frequencies related to the frequency domain strain - - """ - white_noise, frequencies = utils.create_white_noise(sampling_frequency, duration) - frequency_domain_strain = self.__power_spectral_density_interpolated(frequencies) ** 0.5 * white_noise - out_of_bounds = (frequencies < min(self.frequency_array)) | (frequencies > max(self.frequency_array)) - frequency_domain_strain[out_of_bounds] = 0 * (1 + 1j) - return frequency_domain_strain, frequencies - - -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: - interferometer = load_interferometer(filename) - return interferometer - 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): - """ Calculate the safe signal duration, given the parameters - - Parameters - ---------- - mass_1, mass_2, a_1, a_2, tilt_1, tilt_2: float - The signal parameters - flow: float - The lower frequency cutoff from which to calculate the signal duration - - Returns - ------- - safe_signal_duration: float - The shortest safe signal duration (i.e., the signal duration rounded up - to the nearest power of 2) - - """ - chirp_time = lalsim.SimInspiralChirpTimeBound( - flow, mass_1 * lal.MSUN_SI, mass_2 * lal.MSUN_SI, - a_1 * np.cos(tilt_1), a_2 * np.cos(tilt_2)) - return max(2**(int(np.log2(chirp_time)) + 1), 4) - - -def inject_signal_into_gwpy_timeseries( - data, waveform_generator, parameters, det, outdir=None, label=None): - """ Inject a signal into a gwpy timeseries - - Parameters - ---------- - data: gwpy.timeseries.TimeSeries - The time-series data into which we want to inject the signal - waveform_generator: bilby.gw.waveform_generator.WaveformGenerator - An initialised waveform_generator - parameters: dict - A dictionary of the signal-parameters to inject - ifo: bilby.gw.detector.Interferometer - The interferometer for which the data refers too - outdir, label: str - If given, the outdir and label used to generate a plot - - Returns - ------- - data_and_signal: gwpy.timeseries.TimeSeries - The data with the time-domain signal added - meta_data: dict - A dictionary of meta data about the injection - - """ - ifo = get_empty_interferometer(det) - ifo.strain_data.set_from_gwpy_timeseries(data) - - parameters_check, _ = conversion.convert_to_lal_binary_black_hole_parameters(parameters) - safe_time = get_safe_signal_duration(**parameters_check) - if data.duration.value < safe_time: - ValueError( - "Injecting a signal with safe-duration {} longer than the data {}" - .format(safe_time, data.duration.value)) - - waveform_polarizations = waveform_generator.time_domain_strain(parameters) - - signal = np.zeros(len(data)) - - for mode in waveform_polarizations.keys(): - det_response = ifo.antenna_response( - parameters['ra'], parameters['dec'], parameters['geocent_time'], - parameters['psi'], mode) - signal += waveform_polarizations[mode] * det_response - time_shift = ifo.time_delay_from_geocenter( - parameters['ra'], parameters['dec'], parameters['geocent_time']) - - dt = parameters['geocent_time'] + time_shift - data.times[0].value - n_roll = dt * data.sample_rate.value - n_roll = int(np.round(n_roll)) - signal_shifted = gwpy.timeseries.TimeSeries( - data=np.roll(signal, n_roll), times=data.times, unit=data.unit) - - signal_and_data = data.inject(signal_shifted) - - if outdir is not None and label is not None: - fig = gwpy.plot.Plot(signal_shifted) - fig.savefig('{}/{}_{}_time_domain_injected_signal'.format( - outdir, ifo.name, label)) - - meta_data = dict() - frequency_domain_signal, _ = utils.nfft(signal, waveform_generator.sampling_frequency) - meta_data['optimal_SNR'] = ( - np.sqrt(ifo.optimal_snr_squared(signal=frequency_domain_signal)).real) - meta_data['matched_filter_SNR'] = ( - ifo.matched_filter_snr(signal=frequency_domain_signal)) - meta_data['parameters'] = parameters - - logger.info("Injected signal in {}:".format(ifo.name)) - logger.info(" optimal SNR = {:.2f}".format(meta_data['optimal_SNR'])) - logger.info(" matched filter SNR = {:.2f}".format(meta_data['matched_filter_SNR'])) - for key in parameters: - logger.info(' {} = {}'.format(key, parameters[key])) - - return signal_and_data, meta_data diff --git a/bilby/gw/detector/__init__.py b/bilby/gw/detector/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5e057ac6ca7ac0fdf6b06d23056711abec1d0851 --- /dev/null +++ b/bilby/gw/detector/__init__.py @@ -0,0 +1,273 @@ +from bilby.gw import conversion +from .interferometer import * +from .networks import * +from .psd import * +from .strain_data import * + +try: + import lal + import lalsimulation as lalsim +except ImportError: + logger.warning("You do not have lalsuite installed currently. You will" + " not be able to use some of the prebuilt functions.") + + +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 + ---------- + mass_1, mass_2, a_1, a_2, tilt_1, tilt_2: float + The signal parameters + flow: float + The lower frequency cutoff from which to calculate the signal duration + + Returns + ------- + safe_signal_duration: float + The shortest safe signal duration (i.e., the signal duration rounded up + to the nearest power of 2) + + """ + chirp_time = lalsim.SimInspiralChirpTimeBound( + flow, mass_1 * lal.MSUN_SI, mass_2 * lal.MSUN_SI, + a_1 * np.cos(tilt_1), a_2 * np.cos(tilt_2)) + return max(2**(int(np.log2(chirp_time)) + 1), 4) + + +def inject_signal_into_gwpy_timeseries( + data, waveform_generator, parameters, det, outdir=None, label=None): + """ Inject a signal into a gwpy timeseries + + Parameters + ---------- + data: gwpy.timeseries.TimeSeries + The time-series data into which we want to inject the signal + waveform_generator: bilby.gw.waveform_generator.WaveformGenerator + An initialised waveform_generator + parameters: dict + A dictionary of the signal-parameters to inject + ifo: bilby.gw.detector.Interferometer + The interferometer for which the data refers too + outdir, label: str + If given, the outdir and label used to generate a plot + + Returns + ------- + data_and_signal: gwpy.timeseries.TimeSeries + The data with the time-domain signal added + meta_data: dict + A dictionary of meta data about the injection + + """ + ifo = get_empty_interferometer(det) + ifo.strain_data.set_from_gwpy_timeseries(data) + + parameters_check, _ = conversion.convert_to_lal_binary_black_hole_parameters(parameters) + safe_time = get_safe_signal_duration(**parameters_check) + if data.duration.value < safe_time: + ValueError( + "Injecting a signal with safe-duration {} longer than the data {}" + .format(safe_time, data.duration.value)) + + waveform_polarizations = waveform_generator.time_domain_strain(parameters) + + signal = np.zeros(len(data)) + + for mode in waveform_polarizations.keys(): + det_response = ifo.antenna_response( + parameters['ra'], parameters['dec'], parameters['geocent_time'], + parameters['psi'], mode) + signal += waveform_polarizations[mode] * det_response + time_shift = ifo.time_delay_from_geocenter( + parameters['ra'], parameters['dec'], parameters['geocent_time']) + + dt = parameters['geocent_time'] + time_shift - data.times[0].value + n_roll = dt * data.sample_rate.value + n_roll = int(np.round(n_roll)) + signal_shifted = gwpy.timeseries.TimeSeries( + data=np.roll(signal, n_roll), times=data.times, unit=data.unit) + + signal_and_data = data.inject(signal_shifted) + + if outdir is not None and label is not None: + fig = gwpy.plot.Plot(signal_shifted) + fig.savefig('{}/{}_{}_time_domain_injected_signal'.format( + outdir, ifo.name, label)) + + meta_data = dict() + frequency_domain_signal, _ = utils.nfft(signal, waveform_generator.sampling_frequency) + meta_data['optimal_SNR'] = ( + np.sqrt(ifo.optimal_snr_squared(signal=frequency_domain_signal)).real) + meta_data['matched_filter_SNR'] = ( + ifo.matched_filter_snr(signal=frequency_domain_signal)) + meta_data['parameters'] = parameters + + logger.info("Injected signal in {}:".format(ifo.name)) + logger.info(" optimal SNR = {:.2f}".format(meta_data['optimal_SNR'])) + logger.info(" matched filter SNR = {:.2f}".format(meta_data['matched_filter_SNR'])) + for key in parameters: + logger.info(' {} = {}'.format(key, parameters[key])) + + return signal_and_data, meta_data + + +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)) diff --git a/bilby/gw/detectors/CE.interferometer b/bilby/gw/detector/detectors/CE.interferometer similarity index 100% rename from bilby/gw/detectors/CE.interferometer rename to bilby/gw/detector/detectors/CE.interferometer diff --git a/bilby/gw/detectors/ET.interferometer b/bilby/gw/detector/detectors/ET.interferometer similarity index 100% rename from bilby/gw/detectors/ET.interferometer rename to bilby/gw/detector/detectors/ET.interferometer diff --git a/bilby/gw/detectors/GEO600.interferometer b/bilby/gw/detector/detectors/GEO600.interferometer similarity index 100% rename from bilby/gw/detectors/GEO600.interferometer rename to bilby/gw/detector/detectors/GEO600.interferometer diff --git a/bilby/gw/detectors/H1.interferometer b/bilby/gw/detector/detectors/H1.interferometer similarity index 100% rename from bilby/gw/detectors/H1.interferometer rename to bilby/gw/detector/detectors/H1.interferometer diff --git a/bilby/gw/detectors/K1.interferometer b/bilby/gw/detector/detectors/K1.interferometer similarity index 100% rename from bilby/gw/detectors/K1.interferometer rename to bilby/gw/detector/detectors/K1.interferometer diff --git a/bilby/gw/detectors/L1.interferometer b/bilby/gw/detector/detectors/L1.interferometer similarity index 100% rename from bilby/gw/detectors/L1.interferometer rename to bilby/gw/detector/detectors/L1.interferometer diff --git a/bilby/gw/detectors/V1.interferometer b/bilby/gw/detector/detectors/V1.interferometer similarity index 100% rename from bilby/gw/detectors/V1.interferometer rename to bilby/gw/detector/detectors/V1.interferometer diff --git a/bilby/gw/detector/interferometer.py b/bilby/gw/detector/interferometer.py new file mode 100644 index 0000000000000000000000000000000000000000..271090c5e88f63f075e627ab6a266374b217324f --- /dev/null +++ b/bilby/gw/detector/interferometer.py @@ -0,0 +1,933 @@ +import os +import sys + +import numpy as np +from matplotlib import pyplot as plt + +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 .strain_data import InterferometerStrainData + +try: + import gwpy + import gwpy.signal +except ImportError: + logger.warning("You do not have gwpy installed currently. You will " + " not be able to use some of the prebuilt functions.") + + +class Interferometer(object): + """Class for the Interferometer """ + + def __init__(self, name, power_spectral_density, minimum_frequency, maximum_frequency, + length, latitude, longitude, elevation, xarm_azimuth, yarm_azimuth, + xarm_tilt=0., yarm_tilt=0., calibration_model=Recalibrate()): + """ + Instantiate an Interferometer object. + + Parameters + ---------- + name: str + Interferometer name, e.g., H1. + power_spectral_density: bilby.gw.detector.PowerSpectralDensity + Power spectral density determining the sensitivity of the detector. + minimum_frequency: float + Minimum frequency to analyse for detector. + maximum_frequency: float + Maximum frequency to analyse for detector. + length: float + Length of the interferometer in km. + latitude: float + Latitude North in degrees (South is negative). + longitude: float + Longitude East in degrees (West is negative). + elevation: float + Height above surface in metres. + xarm_azimuth: float + Orientation of the x arm in degrees North of East. + yarm_azimuth: float + Orientation of the y arm in degrees North of East. + xarm_tilt: float, optional + Tilt of the x arm in radians above the horizontal defined by + ellipsoid earth model in LIGO-T980044-08. + yarm_tilt: float, optional + Tilt of the y arm in radians above the horizontal. + calibration_model: Recalibration + Calibration model, this applies the calibration correction to the + template, the default model applies no correction. + """ + self.__x_updated = False + self.__y_updated = False + self.__vertex_updated = False + self.__detector_tensor_updated = False + + self.name = name + self.length = length + self.latitude = latitude + self.longitude = longitude + self.elevation = elevation + self.xarm_azimuth = xarm_azimuth + self.yarm_azimuth = yarm_azimuth + self.xarm_tilt = xarm_tilt + self.yarm_tilt = yarm_tilt + self.power_spectral_density = power_spectral_density + self.calibration_model = calibration_model + self._strain_data = InterferometerStrainData( + minimum_frequency=minimum_frequency, + maximum_frequency=maximum_frequency) + self.meta_data = dict() + + def __eq__(self, other): + if self.name == other.name and \ + self.length == other.length and \ + self.latitude == other.latitude and \ + self.longitude == other.longitude and \ + self.elevation == other.elevation and \ + self.xarm_azimuth == other.xarm_azimuth and \ + self.xarm_tilt == other.xarm_tilt and \ + self.yarm_azimuth == other.yarm_azimuth and \ + self.yarm_tilt == other.yarm_tilt and \ + self.power_spectral_density.__eq__(other.power_spectral_density) and \ + self.calibration_model == other.calibration_model and \ + self.strain_data == other.strain_data: + return True + return False + + def __repr__(self): + return self.__class__.__name__ + '(name=\'{}\', power_spectral_density={}, minimum_frequency={}, ' \ + 'maximum_frequency={}, length={}, latitude={}, longitude={}, elevation={}, ' \ + 'xarm_azimuth={}, yarm_azimuth={}, xarm_tilt={}, yarm_tilt={})' \ + .format(self.name, self.power_spectral_density, float(self.minimum_frequency), + float(self.maximum_frequency), float(self.length), float(self.latitude), float(self.longitude), + float(self.elevation), float(self.xarm_azimuth), float(self.yarm_azimuth), float(self.xarm_tilt), + float(self.yarm_tilt)) + + @property + def minimum_frequency(self): + return self.strain_data.minimum_frequency + + @minimum_frequency.setter + def minimum_frequency(self, minimum_frequency): + self._strain_data.minimum_frequency = minimum_frequency + + @property + def maximum_frequency(self): + return self.strain_data.maximum_frequency + + @maximum_frequency.setter + def maximum_frequency(self, maximum_frequency): + self._strain_data.maximum_frequency = maximum_frequency + + @property + def strain_data(self): + """ A bilby.gw.detector.InterferometerStrainData instance """ + return self._strain_data + + @strain_data.setter + def strain_data(self, strain_data): + """ Set the strain_data + + This sets the Interferometer.strain_data equal to the provided + strain_data. This will override the minimum_frequency and + maximum_frequency of the provided strain_data object with those of + the Interferometer object. + """ + strain_data.minimum_frequency = self.minimum_frequency + strain_data.maximum_frequency = self.maximum_frequency + + self._strain_data = strain_data + + def set_strain_data_from_frequency_domain_strain( + self, frequency_domain_strain, sampling_frequency=None, + duration=None, start_time=0, frequency_array=None): + """ Set the `Interferometer.strain_data` from a numpy array + + Parameters + ---------- + frequency_domain_strain: array_like + The data to set. + sampling_frequency: float + The sampling frequency (in Hz). + duration: float + The data duration (in s). + start_time: float + The GPS start-time of the data. + frequency_array: array_like + The array of frequencies, if sampling_frequency and duration not + given. + + """ + self.strain_data.set_from_frequency_domain_strain( + frequency_domain_strain=frequency_domain_strain, + sampling_frequency=sampling_frequency, duration=duration, + start_time=start_time, frequency_array=frequency_array) + + def set_strain_data_from_power_spectral_density( + self, sampling_frequency, duration, start_time=0): + """ Set the `Interferometer.strain_data` from a power spectal density + + This uses the `interferometer.power_spectral_density` object to set + the `strain_data` to a noise realization. See + `bilby.gw.detector.InterferometerStrainData` for further information. + + Parameters + ---------- + sampling_frequency: float + The sampling frequency (in Hz) + duration: float + The data duration (in s) + start_time: float + The GPS start-time of the data + + """ + self.strain_data.set_from_power_spectral_density( + self.power_spectral_density, sampling_frequency=sampling_frequency, + duration=duration, start_time=start_time) + + def set_strain_data_from_frame_file( + self, frame_file, sampling_frequency, duration, start_time=0, + channel=None, buffer_time=1): + """ Set the `Interferometer.strain_data` from a frame file + + Parameters + ---------- + frame_file: str + File from which to load data. + channel: str + Channel to read from frame. + sampling_frequency: float + The sampling frequency (in Hz) + duration: float + The data duration (in s) + start_time: float + The GPS start-time of the data + buffer_time: float + Read in data with `start_time-buffer_time` and + `start_time+duration+buffer_time` + + """ + self.strain_data.set_from_frame_file( + frame_file=frame_file, sampling_frequency=sampling_frequency, + duration=duration, start_time=start_time, + channel=channel, buffer_time=buffer_time) + + def set_strain_data_from_csv(self, filename): + """ Set the `Interferometer.strain_data` from a csv file + + Parameters + ---------- + filename: str + The path to the file to read in + + """ + self.strain_data.set_from_csv(filename) + + def set_strain_data_from_zero_noise( + self, sampling_frequency, duration, start_time=0): + """ Set the `Interferometer.strain_data` to zero noise + + Parameters + ---------- + sampling_frequency: float + The sampling frequency (in Hz) + duration: float + The data duration (in s) + start_time: float + The GPS start-time of the data + + """ + + self.strain_data.set_from_zero_noise( + sampling_frequency=sampling_frequency, duration=duration, + start_time=start_time) + + @property + def latitude(self): + """ Saves latitude in rad internally. Updates related quantities if set to a different value. + + Returns + ------- + float: The latitude position of the detector in degree + """ + return self.__latitude * 180 / np.pi + + @latitude.setter + def latitude(self, latitude): + self.__latitude = latitude * np.pi / 180 + self.__x_updated = False + self.__y_updated = False + self.__vertex_updated = False + + @property + def longitude(self): + """ Saves longitude in rad internally. Updates related quantities if set to a different value. + + Returns + ------- + float: The longitude position of the detector in degree + """ + return self.__longitude * 180 / np.pi + + @longitude.setter + def longitude(self, longitude): + self.__longitude = longitude * np.pi / 180 + self.__x_updated = False + self.__y_updated = False + self.__vertex_updated = False + + @property + def elevation(self): + """ Updates related quantities if set to a different values. + + Returns + ------- + float: The height about the surface in meters + """ + return self.__elevation + + @elevation.setter + def elevation(self, elevation): + self.__elevation = elevation + self.__vertex_updated = False + + @property + def xarm_azimuth(self): + """ Saves the x-arm azimuth in rad internally. Updates related quantities if set to a different values. + + Returns + ------- + float: The x-arm azimuth in degrees. + + """ + return self.__xarm_azimuth * 180 / np.pi + + @xarm_azimuth.setter + def xarm_azimuth(self, xarm_azimuth): + self.__xarm_azimuth = xarm_azimuth * np.pi / 180 + self.__x_updated = False + + @property + def yarm_azimuth(self): + """ Saves the y-arm azimuth in rad internally. Updates related quantities if set to a different values. + + Returns + ------- + float: The y-arm azimuth in degrees. + + """ + return self.__yarm_azimuth * 180 / np.pi + + @yarm_azimuth.setter + def yarm_azimuth(self, yarm_azimuth): + self.__yarm_azimuth = yarm_azimuth * np.pi / 180 + self.__y_updated = False + + @property + def xarm_tilt(self): + """ Updates related quantities if set to a different values. + + Returns + ------- + float: The x-arm tilt in radians. + + """ + return self.__xarm_tilt + + @xarm_tilt.setter + def xarm_tilt(self, xarm_tilt): + self.__xarm_tilt = xarm_tilt + self.__x_updated = False + + @property + def yarm_tilt(self): + """ Updates related quantities if set to a different values. + + Returns + ------- + float: The y-arm tilt in radians. + + """ + return self.__yarm_tilt + + @yarm_tilt.setter + def yarm_tilt(self, yarm_tilt): + self.__yarm_tilt = yarm_tilt + self.__y_updated = False + + @property + def vertex(self): + """ Position of the IFO vertex in geocentric coordinates in meters. + + Is automatically updated if related quantities are modified. + + Returns + ------- + array_like: A 3D array representation of the vertex + """ + if not self.__vertex_updated: + self.__vertex = gwutils.get_vertex_position_geocentric(self.__latitude, self.__longitude, + self.elevation) + self.__vertex_updated = True + return self.__vertex + + @property + def x(self): + """ A unit vector along the x-arm + + Is automatically updated if related quantities are modified. + + Returns + ------- + array_like: A 3D array representation of a unit vector along the x-arm + + """ + if not self.__x_updated: + self.__x = self.unit_vector_along_arm('x') + self.__x_updated = True + self.__detector_tensor_updated = False + return self.__x + + @property + def y(self): + """ A unit vector along the y-arm + + Is automatically updated if related quantities are modified. + + Returns + ------- + array_like: A 3D array representation of a unit vector along the y-arm + + """ + if not self.__y_updated: + self.__y = self.unit_vector_along_arm('y') + self.__y_updated = True + self.__detector_tensor_updated = False + return self.__y + + @property + def detector_tensor(self): + """ + Calculate the detector tensor from the unit vectors along each arm of the detector. + + See Eq. B6 of arXiv:gr-qc/0008066 + + Is automatically updated if related quantities are modified. + + Returns + ------- + array_like: A 3x3 array representation of the detector tensor + + """ + if not self.__x_updated or not self.__y_updated: + _, _ = self.x, self.y # noqa + if not self.__detector_tensor_updated: + self.__detector_tensor = 0.5 * (np.einsum('i,j->ij', self.x, self.x) - np.einsum('i,j->ij', self.y, self.y)) + self.__detector_tensor_updated = True + return self.__detector_tensor + + def antenna_response(self, ra, dec, time, psi, mode): + """ + Calculate the antenna response function for a given sky location + + See Nishizawa et al. (2009) arXiv:0903.0528 for definitions of the polarisation tensors. + [u, v, w] represent the Earth-frame + [m, n, omega] represent the wave-frame + Note: there is a typo in the definition of the wave-frame in Nishizawa et al. + + Parameters + ------- + ra: float + right ascension in radians + dec: float + declination in radians + time: float + geocentric GPS time + psi: float + binary polarisation angle counter-clockwise about the direction of propagation + mode: str + polarisation mode (e.g. 'plus', 'cross') + + Returns + ------- + array_like: A 3x3 array representation of the antenna response for the specified mode + + """ + polarization_tensor = gwutils.get_polarization_tensor(ra, dec, time, psi, mode) + return np.einsum('ij,ij->', self.detector_tensor, polarization_tensor) + + def get_detector_response(self, waveform_polarizations, parameters): + """ Get the detector response for a particular waveform + + Parameters + ------- + waveform_polarizations: dict + polarizations of the waveform + parameters: dict + parameters describing position and time of arrival of the signal + + Returns + ------- + array_like: A 3x3 array representation of the detector response (signal observed in the interferometer) + """ + signal = {} + for mode in waveform_polarizations.keys(): + det_response = self.antenna_response( + parameters['ra'], + parameters['dec'], + parameters['geocent_time'], + parameters['psi'], mode) + + signal[mode] = waveform_polarizations[mode] * det_response + signal_ifo = sum(signal.values()) + + signal_ifo *= self.strain_data.frequency_mask + + time_shift = self.time_delay_from_geocenter( + parameters['ra'], parameters['dec'], parameters['geocent_time']) + dt = parameters['geocent_time'] + time_shift - self.strain_data.start_time + + signal_ifo = signal_ifo * np.exp( + -1j * 2 * np.pi * dt * self.frequency_array) + + signal_ifo *= self.calibration_model.get_calibration_factor( + self.frequency_array, prefix='recalib_{}_'.format(self.name), **parameters) + + return signal_ifo + + def inject_signal(self, parameters=None, injection_polarizations=None, + waveform_generator=None): + """ Inject a signal into noise + + Parameters + ---------- + parameters: dict + Parameters of the injection. + 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. + + Note + ------- + if your signal takes a substantial amount of time to generate, or + you experience buggy behaviour. It is preferable to provide the + injection_polarizations directly. + + Returns + ------- + injection_polarizations: dict + + """ + + if injection_polarizations is None: + if waveform_generator is not None: + injection_polarizations = \ + waveform_generator.frequency_domain_strain(parameters) + else: + raise ValueError( + "inject_signal needs one of waveform_generator or " + "injection_polarizations.") + + if injection_polarizations is None: + raise ValueError( + 'Trying to inject signal which is None. The most likely cause' + ' is that waveform_generator.frequency_domain_strain returned' + ' None. This can be caused if, e.g., mass_2 > mass_1.') + + if not self.strain_data.time_within_data(parameters['geocent_time']): + logger.warning( + 'Injecting signal outside segment, start_time={}, merger time={}.' + .format(self.strain_data.start_time, parameters['geocent_time'])) + + signal_ifo = self.get_detector_response(injection_polarizations, parameters) + if np.shape(self.frequency_domain_strain).__eq__(np.shape(signal_ifo)): + self.strain_data.frequency_domain_strain = \ + signal_ifo + self.strain_data.frequency_domain_strain + else: + logger.info('Injecting into zero noise.') + self.set_strain_data_from_frequency_domain_strain( + signal_ifo, + sampling_frequency=self.strain_data.sampling_frequency, + duration=self.strain_data.duration, + start_time=self.strain_data.start_time) + + self.meta_data['optimal_SNR'] = ( + np.sqrt(self.optimal_snr_squared(signal=signal_ifo)).real) + self.meta_data['matched_filter_SNR'] = ( + self.matched_filter_snr(signal=signal_ifo)) + self.meta_data['parameters'] = parameters + + logger.info("Injected signal in {}:".format(self.name)) + logger.info(" optimal SNR = {:.2f}".format(self.meta_data['optimal_SNR'])) + logger.info(" matched filter SNR = {:.2f}".format(self.meta_data['matched_filter_SNR'])) + for key in parameters: + logger.info(' {} = {}'.format(key, parameters[key])) + + return injection_polarizations + + def unit_vector_along_arm(self, arm): + """ + Calculate the unit vector pointing along the specified arm in cartesian Earth-based coordinates. + + See Eqs. B14-B17 in arXiv:gr-qc/0008066 + + Parameters + ------- + arm: str + 'x' or 'y' (arm of the detector) + + Returns + ------- + array_like: 3D unit vector along arm in cartesian Earth-based coordinates + + Raises + ------- + ValueError: If arm is neither 'x' nor 'y' + + """ + if arm == 'x': + return self.__calculate_arm(self.__xarm_tilt, self.__xarm_azimuth) + elif arm == 'y': + return self.__calculate_arm(self.__yarm_tilt, self.__yarm_azimuth) + else: + raise ValueError("Arm must either be 'x' or 'y'.") + + def __calculate_arm(self, arm_tilt, arm_azimuth): + e_long = np.array([-np.sin(self.__longitude), np.cos(self.__longitude), 0]) + e_lat = np.array([-np.sin(self.__latitude) * np.cos(self.__longitude), + -np.sin(self.__latitude) * np.sin(self.__longitude), np.cos(self.__latitude)]) + e_h = np.array([np.cos(self.__latitude) * np.cos(self.__longitude), + np.cos(self.__latitude) * np.sin(self.__longitude), np.sin(self.__latitude)]) + + return (np.cos(arm_tilt) * np.cos(arm_azimuth) * e_long + + np.cos(arm_tilt) * np.sin(arm_azimuth) * e_lat + + np.sin(arm_tilt) * e_h) + + @property + def amplitude_spectral_density_array(self): + """ Returns the amplitude spectral density (ASD) given we know a power spectral denstiy (PSD) + + Returns + ------- + array_like: An array representation of the ASD + + """ + return self.power_spectral_density_array ** 0.5 + + @property + def power_spectral_density_array(self): + """ Returns the power spectral density (PSD) + + This accounts for whether the data in the interferometer has been windowed. + + Returns + ------- + array_like: An array representation of the PSD + + """ + return (self.power_spectral_density.power_spectral_density_interpolated(self.frequency_array) * + self.strain_data.window_factor) + + @property + def frequency_array(self): + return self.strain_data.frequency_array + + @property + def frequency_mask(self): + return self.strain_data.frequency_mask + + @property + def frequency_domain_strain(self): + """ The frequency domain strain in units of strain / Hz """ + return self.strain_data.frequency_domain_strain + + @property + def time_domain_strain(self): + """ The time domain strain in units of s """ + return self.strain_data.time_domain_strain + + @property + def time_array(self): + return self.strain_data.time_array + + def time_delay_from_geocenter(self, ra, dec, time): + """ + Calculate the time delay from the geocenter for the interferometer. + + Use the time delay function from utils. + + Parameters + ------- + ra: float + right ascension of source in radians + dec: float + declination of source in radians + time: float + GPS time + + Returns + ------- + float: The time delay from geocenter in seconds + """ + return gwutils.time_delay_geocentric(self.vertex, np.array([0, 0, 0]), ra, dec, time) + + def vertex_position_geocentric(self): + """ + Calculate the position of the IFO vertex in geocentric coordinates in meters. + + Based on arXiv:gr-qc/0008066 Eqs. B11-B13 except for the typo in the definition of the local radius. + See Section 2.1 of LIGO-T980044-10 for the correct expression + + Returns + ------- + array_like: A 3D array representation of the vertex + """ + return gwutils.get_vertex_position_geocentric(self.__latitude, self.__longitude, self.__elevation) + + def optimal_snr_squared(self, signal): + """ + + Parameters + ---------- + signal: array_like + Array containing the signal + + Returns + ------- + float: The optimal signal to noise ratio possible squared + """ + return gwutils.optimal_snr_squared( + signal=signal[self.frequency_mask], + power_spectral_density=self.power_spectral_density_array[self.frequency_mask], + duration=self.strain_data.duration) + + def inner_product(self, signal): + """ + + Parameters + ---------- + signal: array_like + Array containing the signal + + Returns + ------- + float: The optimal signal to noise ratio possible squared + """ + return gwutils.noise_weighted_inner_product( + aa=signal[self.frequency_mask], + bb=self.frequency_domain_strain[self.frequency_mask], + power_spectral_density=self.power_spectral_density_array[self.frequency_mask], + duration=self.strain_data.duration) + + def matched_filter_snr(self, signal): + """ + + Parameters + ---------- + signal: array_like + Array containing the signal + + Returns + ------- + float: The matched filter signal to noise ratio squared + + """ + return gwutils.matched_filter_snr( + signal=signal[self.frequency_mask], + frequency_domain_strain=self.frequency_domain_strain[self.frequency_mask], + power_spectral_density=self.power_spectral_density_array[self.frequency_mask], + duration=self.strain_data.duration) + + @property + def whitened_frequency_domain_strain(self): + """ Calculates the whitened data by dividing data by the amplitude spectral density + + Returns + ------- + array_like: The whitened data + """ + return self.strain_data.frequency_domain_strain / self.amplitude_spectral_density_array + + def save_data(self, outdir, label=None): + """ Creates a save file for the data in plain text format + + Parameters + ---------- + outdir: str + The output directory in which the data is supposed to be saved + label: str + The name of the output files + """ + + if label is None: + filename_psd = '{}/{}_psd.dat'.format(outdir, self.name) + filename_data = '{}/{}_frequency_domain_data.dat'.format(outdir, self.name) + else: + filename_psd = '{}/{}_{}_psd.dat'.format(outdir, self.name, label) + filename_data = '{}/{}_{}_frequency_domain_data.dat'.format(outdir, self.name, label) + np.savetxt(filename_data, + np.array( + [self.frequency_array, + self.frequency_domain_strain.real, + self.frequency_domain_strain.imag]).T, + header='f real_h(f) imag_h(f)') + np.savetxt(filename_psd, + np.array( + [self.frequency_array, + self.amplitude_spectral_density_array]).T, + header='f h(f)') + + def plot_data(self, signal=None, outdir='.', label=None): + if utils.command_line_args.test: + return + + fig, ax = plt.subplots() + df = self.frequency_array[1] - self.frequency_array[0] + asd = gwutils.asd_from_freq_series( + freq_data=self.frequency_domain_strain, df=df) + + ax.loglog(self.frequency_array[self.frequency_mask], + asd[self.frequency_mask], + color='C0', label=self.name) + ax.loglog(self.frequency_array[self.frequency_mask], + self.amplitude_spectral_density_array[self.frequency_mask], + color='C1', lw=1.0, label=self.name + ' ASD') + if signal is not None: + signal_asd = gwutils.asd_from_freq_series( + freq_data=signal, df=df) + + ax.loglog(self.frequency_array[self.frequency_mask], + signal_asd[self.frequency_mask], + color='C2', + label='Signal') + ax.grid(True) + ax.set_ylabel(r'Strain [strain/$\sqrt{\rm Hz}$]') + ax.set_xlabel(r'Frequency [Hz]') + ax.legend(loc='best') + fig.tight_layout() + if label is None: + fig.savefig( + '{}/{}_frequency_domain_data.png'.format(outdir, self.name)) + else: + fig.savefig( + '{}/{}_{}_frequency_domain_data.png'.format( + outdir, self.name, label)) + plt.close(fig) + + def plot_time_domain_data( + self, outdir='.', label=None, bandpass_frequencies=(50, 250), + notches=None, start_end=None, t0=None): + """ Plots the strain data in the time domain + + Parameters + ---------- + outdir, label: str + Used in setting the saved filename. + bandpass: tuple, optional + A tuple of the (low, high) frequencies to use when bandpassing the + data, if None no bandpass is applied. + notches: list, optional + A list of frequencies specifying any lines to notch + start_end: tuple + A tuple of the (start, end) range of GPS times to plot + t0: float + If given, the reference time to subtract from the time series before + plotting. + + """ + + # We use the gwpy timeseries to perform bandpass and notching + if notches is None: + notches = list() + timeseries = gwpy.timeseries.TimeSeries( + data=self.time_domain_strain, times=self.time_array) + zpks = [] + if bandpass_frequencies is not None: + zpks.append(gwpy.signal.filter_design.bandpass( + bandpass_frequencies[0], bandpass_frequencies[1], + self.strain_data.sampling_frequency)) + if notches is not None: + for line in notches: + zpks.append(gwpy.signal.filter_design.notch( + line, self.strain_data.sampling_frequency)) + if len(zpks) > 0: + zpk = gwpy.signal.filter_design.concatenate_zpks(*zpks) + strain = timeseries.filter(zpk, filtfilt=True) + else: + strain = timeseries + + fig, ax = plt.subplots() + + if t0: + x = self.time_array - t0 + xlabel = 'GPS time [s] - {}'.format(t0) + else: + x = self.time_array + xlabel = 'GPS time [s]' + + ax.plot(x, strain) + ax.set_xlabel(xlabel) + ax.set_ylabel('Strain') + + if start_end is not None: + ax.set_xlim(*start_end) + + fig.tight_layout() + + if label is None: + fig.savefig( + '{}/{}_time_domain_data.png'.format(outdir, self.name)) + else: + fig.savefig( + '{}/{}_{}_time_domain_data.png'.format(outdir, self.name, label)) + plt.close(fig) + + @staticmethod + def _hdf5_filename_from_outdir_label(outdir, label): + return os.path.join(outdir, label + '.h5') + + def to_hdf5(self, outdir='outdir', label=None): + """ Save the object to a hdf5 file + + Attributes + ---------- + outdir: str, optional + Output directory name of the file, defaults to 'outdir'. + label: str, optional + Output file name, is self.name if not given otherwise. + """ + import deepdish + if sys.version_info[0] < 3: + raise NotImplementedError('Pickling of Interferometer is not supported in Python 2.' + 'Use Python 3 instead.') + if label is None: + label = self.name + utils.check_directory_exists_and_if_not_mkdir('outdir') + filename = self._hdf5_filename_from_outdir_label(outdir, label) + deepdish.io.save(filename, self) + + @classmethod + def from_hdf5(cls, filename=None): + """ Loads in an Interferometer object from an hdf5 file + + Parameters + ---------- + filename: str + If given, try to load from this filename + + """ + import deepdish + if sys.version_info[0] < 3: + raise NotImplementedError('Pickling of Interferometer is not supported in Python 2.' + 'Use Python 3 instead.') + + res = deepdish.io.load(filename) + if res.__class__ != cls: + raise TypeError('The loaded object is not an Interferometer') + return res diff --git a/bilby/gw/detector/networks.py b/bilby/gw/detector/networks.py new file mode 100644 index 0000000000000000000000000000000000000000..ee91fe80ad069b15e0b3879b85ad9e6e6da5d969 --- /dev/null +++ b/bilby/gw/detector/networks.py @@ -0,0 +1,472 @@ +import os +import sys + +import numpy as np + +from bilby.core import utils +from bilby.core.utils import logger +from .interferometer import Interferometer +from .psd import PowerSpectralDensity +from .strain_data import InterferometerStrainData + + +class InterferometerList(list): + """ A list of Interferometer objects """ + + def __init__(self, interferometers): + """ Instantiate a InterferometerList + + The InterferometerList is a list of Interferometer objects, each + object has the data used in evaluating the likelihood + + Parameters + ---------- + interferometers: iterable + The list of interferometers + """ + + list.__init__(self) + if type(interferometers) == str: + raise TypeError("Input must not be a string") + for ifo in interferometers: + if type(ifo) == str: + ifo = get_empty_interferometer(ifo) + if type(ifo) not in [Interferometer, TriangularInterferometer]: + raise TypeError("Input list of interferometers are not all Interferometer objects") + else: + self.append(ifo) + self._check_interferometers() + + def _check_interferometers(self): + """ Check certain aspects of the set are the same """ + consistent_attributes = ['duration', 'start_time', 'sampling_frequency'] + for attribute in consistent_attributes: + x = [getattr(interferometer.strain_data, attribute) + for interferometer in self] + if not all(y == x[0] for y in x): + raise ValueError("The {} of all interferometers are not the same".format(attribute)) + + def set_strain_data_from_power_spectral_densities(self, sampling_frequency, duration, start_time=0): + """ Set the `Interferometer.strain_data` from the power spectral densities of the detectors + + This uses the `interferometer.power_spectral_density` object to set + the `strain_data` to a noise realization. See + `bilby.gw.detector.InterferometerStrainData` for further information. + + Parameters + ---------- + sampling_frequency: float + The sampling frequency (in Hz) + duration: float + The data duration (in s) + start_time: float + The GPS start-time of the data + + """ + for interferometer in self: + interferometer.set_strain_data_from_power_spectral_density(sampling_frequency=sampling_frequency, + duration=duration, + start_time=start_time) + + def set_strain_data_from_zero_noise(self, sampling_frequency, duration, start_time=0): + """ Set the `Interferometer.strain_data` from the power spectral densities of the detectors + + This uses the `interferometer.power_spectral_density` object to set + the `strain_data` to zero noise. See + `bilby.gw.detector.InterferometerStrainData` for further information. + + Parameters + ---------- + sampling_frequency: float + The sampling frequency (in Hz) + duration: float + The data duration (in s) + start_time: float + The GPS start-time of the data + + """ + for interferometer in self: + interferometer.set_strain_data_from_zero_noise(sampling_frequency=sampling_frequency, + duration=duration, + start_time=start_time) + + def inject_signal(self, parameters=None, injection_polarizations=None, waveform_generator=None): + """ Inject a signal into noise in each of the three detectors. + + Parameters + ---------- + parameters: dict + Parameters of the injection. + 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. + + Note + ---------- + if your signal takes a substantial amount of time to generate, or + you experience buggy behaviour. It is preferable to provide the + injection_polarizations directly. + + Returns + ------- + injection_polarizations: dict + + """ + if injection_polarizations is None: + if waveform_generator is not None: + injection_polarizations = \ + waveform_generator.frequency_domain_strain(parameters) + else: + raise ValueError( + "inject_signal needs one of waveform_generator or " + "injection_polarizations.") + + all_injection_polarizations = list() + for interferometer in self: + all_injection_polarizations.append( + interferometer.inject_signal(parameters=parameters, injection_polarizations=injection_polarizations)) + + return all_injection_polarizations + + def save_data(self, outdir, label=None): + """ Creates a save file for the data in plain text format + + Parameters + ---------- + outdir: str + The output directory in which the data is supposed to be saved + label: str + The string labelling the data + """ + for interferometer in self: + interferometer.save_data(outdir=outdir, label=label) + + def plot_data(self, signal=None, outdir='.', label=None): + if utils.command_line_args.test: + return + + for interferometer in self: + interferometer.plot_data(signal=signal, outdir=outdir, label=label) + + @property + def number_of_interferometers(self): + return len(self) + + @property + def duration(self): + return self[0].strain_data.duration + + @property + def start_time(self): + return self[0].strain_data.start_time + + @property + def sampling_frequency(self): + return self[0].strain_data.sampling_frequency + + @property + def frequency_array(self): + return self[0].strain_data.frequency_array + + def append(self, interferometer): + if isinstance(interferometer, InterferometerList): + super(InterferometerList, self).extend(interferometer) + else: + super(InterferometerList, self).append(interferometer) + self._check_interferometers() + + def extend(self, interferometers): + super(InterferometerList, self).extend(interferometers) + self._check_interferometers() + + def insert(self, index, interferometer): + super(InterferometerList, self).insert(index, interferometer) + self._check_interferometers() + + @property + def meta_data(self): + """ Dictionary of the per-interferometer meta_data """ + return {interferometer.name: interferometer.meta_data + for interferometer in self} + + @staticmethod + def _hdf5_filename_from_outdir_label(outdir, label): + return os.path.join(outdir, label + '.h5') + + def to_hdf5(self, outdir='outdir', label='ifo_list'): + """ Saves the object to a hdf5 file + + Parameters + ---------- + outdir: str, optional + Output directory name of the file + label: str, optional + Output file name, is 'ifo_list' if not given otherwise. A list of + the included interferometers will be appended. + """ + import deepdish + if sys.version_info[0] < 3: + raise NotImplementedError('Pickling of InterferometerList is not supported in Python 2.' + 'Use Python 3 instead.') + label = label + '_' + ''.join(ifo.name for ifo in self) + utils.check_directory_exists_and_if_not_mkdir(outdir) + deepdish.io.save(self._hdf5_filename_from_outdir_label(outdir, label), self) + + @classmethod + def from_hdf5(cls, filename=None): + """ Loads in an InterferometerList object from an hdf5 file + + Parameters + ---------- + filename: str + If given, try to load from this filename + + """ + import deepdish + if sys.version_info[0] < 3: + raise NotImplementedError('Pickling of InterferometerList is not supported in Python 2.' + 'Use Python 3 instead.') + res = deepdish.io.load(filename) + if res.__class__ == list: + res = cls(res) + if res.__class__ != cls: + raise TypeError('The loaded object is not a InterferometerList') + return res + + +class TriangularInterferometer(InterferometerList): + + def __init__(self, name, power_spectral_density, minimum_frequency, maximum_frequency, + length, latitude, longitude, elevation, xarm_azimuth, yarm_azimuth, + xarm_tilt=0., yarm_tilt=0.): + InterferometerList.__init__(self, []) + self.name = name + # for attr in ['power_spectral_density', 'minimum_frequency', 'maximum_frequency']: + if isinstance(power_spectral_density, PowerSpectralDensity): + power_spectral_density = [power_spectral_density] * 3 + if isinstance(minimum_frequency, float) or isinstance(minimum_frequency, int): + minimum_frequency = [minimum_frequency] * 3 + if isinstance(maximum_frequency, float) or isinstance(maximum_frequency, int): + maximum_frequency = [maximum_frequency] * 3 + + for ii in range(3): + self.append(Interferometer( + '{}{}'.format(name, ii + 1), power_spectral_density[ii], minimum_frequency[ii], maximum_frequency[ii], + length, latitude, longitude, elevation, xarm_azimuth, yarm_azimuth, xarm_tilt, yarm_tilt)) + + xarm_azimuth += 240 + yarm_azimuth += 240 + + 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 = utils.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 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(): + ifo = Interferometer(**parameters) + logger.debug('Assuming L shape for {}'.format('name')) + elif parameters['shape'].lower() in ['l', 'ligo']: + parameters.pop('shape') + ifo = Interferometer(**parameters) + elif parameters['shape'].lower() in ['triangular', 'triangle']: + parameters.pop('shape') + ifo = TriangularInterferometer(**parameters) + else: + raise IOError("{} could not be loaded. Invalid parameter 'shape'.".format(filename)) + return ifo + + +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 diff --git a/bilby/gw/noise_curves/AdV_asd.txt b/bilby/gw/detector/noise_curves/AdV_asd.txt similarity index 100% rename from bilby/gw/noise_curves/AdV_asd.txt rename to bilby/gw/detector/noise_curves/AdV_asd.txt diff --git a/bilby/gw/noise_curves/AdV_psd.txt b/bilby/gw/detector/noise_curves/AdV_psd.txt similarity index 100% rename from bilby/gw/noise_curves/AdV_psd.txt rename to bilby/gw/detector/noise_curves/AdV_psd.txt diff --git a/bilby/gw/noise_curves/Aplus_asd.txt b/bilby/gw/detector/noise_curves/Aplus_asd.txt similarity index 100% rename from bilby/gw/noise_curves/Aplus_asd.txt rename to bilby/gw/detector/noise_curves/Aplus_asd.txt diff --git a/bilby/gw/noise_curves/CE_asd.txt b/bilby/gw/detector/noise_curves/CE_asd.txt similarity index 100% rename from bilby/gw/noise_curves/CE_asd.txt rename to bilby/gw/detector/noise_curves/CE_asd.txt diff --git a/bilby/gw/noise_curves/CE_psd.txt b/bilby/gw/detector/noise_curves/CE_psd.txt similarity index 100% rename from bilby/gw/noise_curves/CE_psd.txt rename to bilby/gw/detector/noise_curves/CE_psd.txt diff --git a/bilby/gw/noise_curves/CE_wb_asd.txt b/bilby/gw/detector/noise_curves/CE_wb_asd.txt similarity index 100% rename from bilby/gw/noise_curves/CE_wb_asd.txt rename to bilby/gw/detector/noise_curves/CE_wb_asd.txt diff --git a/bilby/gw/noise_curves/CE_wb_psd.txt b/bilby/gw/detector/noise_curves/CE_wb_psd.txt similarity index 100% rename from bilby/gw/noise_curves/CE_wb_psd.txt rename to bilby/gw/detector/noise_curves/CE_wb_psd.txt diff --git a/bilby/gw/noise_curves/ET_B_asd.txt b/bilby/gw/detector/noise_curves/ET_B_asd.txt similarity index 100% rename from bilby/gw/noise_curves/ET_B_asd.txt rename to bilby/gw/detector/noise_curves/ET_B_asd.txt diff --git a/bilby/gw/noise_curves/ET_B_psd.txt b/bilby/gw/detector/noise_curves/ET_B_psd.txt similarity index 100% rename from bilby/gw/noise_curves/ET_B_psd.txt rename to bilby/gw/detector/noise_curves/ET_B_psd.txt diff --git a/bilby/gw/noise_curves/ET_D_asd.txt b/bilby/gw/detector/noise_curves/ET_D_asd.txt similarity index 100% rename from bilby/gw/noise_curves/ET_D_asd.txt rename to bilby/gw/detector/noise_curves/ET_D_asd.txt diff --git a/bilby/gw/noise_curves/ET_D_psd.txt b/bilby/gw/detector/noise_curves/ET_D_psd.txt similarity index 100% rename from bilby/gw/noise_curves/ET_D_psd.txt rename to bilby/gw/detector/noise_curves/ET_D_psd.txt diff --git a/bilby/gw/noise_curves/GEO600_S6e_asd.txt b/bilby/gw/detector/noise_curves/GEO600_S6e_asd.txt similarity index 100% rename from bilby/gw/noise_curves/GEO600_S6e_asd.txt rename to bilby/gw/detector/noise_curves/GEO600_S6e_asd.txt diff --git a/bilby/gw/noise_curves/KAGRA_design_asd.txt b/bilby/gw/detector/noise_curves/KAGRA_design_asd.txt similarity index 100% rename from bilby/gw/noise_curves/KAGRA_design_asd.txt rename to bilby/gw/detector/noise_curves/KAGRA_design_asd.txt diff --git a/bilby/gw/noise_curves/KAGRA_design_psd.txt b/bilby/gw/detector/noise_curves/KAGRA_design_psd.txt similarity index 100% rename from bilby/gw/noise_curves/KAGRA_design_psd.txt rename to bilby/gw/detector/noise_curves/KAGRA_design_psd.txt diff --git a/bilby/gw/noise_curves/LIGO_srd_asd.txt b/bilby/gw/detector/noise_curves/LIGO_srd_asd.txt similarity index 100% rename from bilby/gw/noise_curves/LIGO_srd_asd.txt rename to bilby/gw/detector/noise_curves/LIGO_srd_asd.txt diff --git a/bilby/gw/noise_curves/LIGO_srd_psd.txt b/bilby/gw/detector/noise_curves/LIGO_srd_psd.txt similarity index 100% rename from bilby/gw/noise_curves/LIGO_srd_psd.txt rename to bilby/gw/detector/noise_curves/LIGO_srd_psd.txt diff --git a/bilby/gw/noise_curves/README.md b/bilby/gw/detector/noise_curves/README.md similarity index 100% rename from bilby/gw/noise_curves/README.md rename to bilby/gw/detector/noise_curves/README.md diff --git a/bilby/gw/noise_curves/aLIGO_ZERO_DET_high_P_asd.txt b/bilby/gw/detector/noise_curves/aLIGO_ZERO_DET_high_P_asd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_ZERO_DET_high_P_asd.txt rename to bilby/gw/detector/noise_curves/aLIGO_ZERO_DET_high_P_asd.txt diff --git a/bilby/gw/noise_curves/aLIGO_ZERO_DET_high_P_psd.txt b/bilby/gw/detector/noise_curves/aLIGO_ZERO_DET_high_P_psd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_ZERO_DET_high_P_psd.txt rename to bilby/gw/detector/noise_curves/aLIGO_ZERO_DET_high_P_psd.txt diff --git a/bilby/gw/noise_curves/aLIGO_early_asd.txt b/bilby/gw/detector/noise_curves/aLIGO_early_asd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_early_asd.txt rename to bilby/gw/detector/noise_curves/aLIGO_early_asd.txt diff --git a/bilby/gw/noise_curves/aLIGO_early_high_asd.txt b/bilby/gw/detector/noise_curves/aLIGO_early_high_asd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_early_high_asd.txt rename to bilby/gw/detector/noise_curves/aLIGO_early_high_asd.txt diff --git a/bilby/gw/noise_curves/aLIGO_early_high_psd.txt b/bilby/gw/detector/noise_curves/aLIGO_early_high_psd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_early_high_psd.txt rename to bilby/gw/detector/noise_curves/aLIGO_early_high_psd.txt diff --git a/bilby/gw/noise_curves/aLIGO_early_psd.txt b/bilby/gw/detector/noise_curves/aLIGO_early_psd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_early_psd.txt rename to bilby/gw/detector/noise_curves/aLIGO_early_psd.txt diff --git a/bilby/gw/noise_curves/aLIGO_late_asd.txt b/bilby/gw/detector/noise_curves/aLIGO_late_asd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_late_asd.txt rename to bilby/gw/detector/noise_curves/aLIGO_late_asd.txt diff --git a/bilby/gw/noise_curves/aLIGO_late_psd.txt b/bilby/gw/detector/noise_curves/aLIGO_late_psd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_late_psd.txt rename to bilby/gw/detector/noise_curves/aLIGO_late_psd.txt diff --git a/bilby/gw/noise_curves/aLIGO_mid_asd.txt b/bilby/gw/detector/noise_curves/aLIGO_mid_asd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_mid_asd.txt rename to bilby/gw/detector/noise_curves/aLIGO_mid_asd.txt diff --git a/bilby/gw/noise_curves/aLIGO_mid_psd.txt b/bilby/gw/detector/noise_curves/aLIGO_mid_psd.txt similarity index 100% rename from bilby/gw/noise_curves/aLIGO_mid_psd.txt rename to bilby/gw/detector/noise_curves/aLIGO_mid_psd.txt diff --git a/bilby/gw/detector/noise_curves/aplus.txt b/bilby/gw/detector/noise_curves/aplus.txt new file mode 100644 index 0000000000000000000000000000000000000000..38f0712c2097a080ab39633c662055f769c141e4 --- /dev/null +++ b/bilby/gw/detector/noise_curves/aplus.txt @@ -0,0 +1,3000 @@ +5.0000000e+00 1.9995145e-20 +5.0115300e+00 1.9459998e-20 +5.0230867e+00 1.8940035e-20 +5.0346699e+00 1.8434796e-20 +5.0462799e+00 1.7943836e-20 +5.0579167e+00 1.7466727e-20 +5.0695803e+00 1.7003051e-20 +5.0812708e+00 1.6552407e-20 +5.0929882e+00 1.6114404e-20 +5.1047327e+00 1.5688666e-20 +5.1165042e+00 1.5274828e-20 +5.1283029e+00 1.4872536e-20 +5.1401288e+00 1.4481449e-20 +5.1519820e+00 1.4101234e-20 +5.1638625e+00 1.3731571e-20 +5.1757704e+00 1.3372150e-20 +5.1877058e+00 1.3022669e-20 +5.1996687e+00 1.2682837e-20 +5.2116592e+00 1.2352372e-20 +5.2236773e+00 1.2031001e-20 +5.2357231e+00 1.1718457e-20 +5.2477968e+00 1.1414485e-20 +5.2598982e+00 1.1118836e-20 +5.2720276e+00 1.0831269e-20 +5.2841849e+00 1.0551549e-20 +5.2963703e+00 1.0279452e-20 +5.3085838e+00 1.0014756e-20 +5.3208254e+00 9.7572503e-21 +5.3330953e+00 9.5067276e-21 +5.3453934e+00 9.2629883e-21 +5.3577199e+00 9.0258384e-21 +5.3700749e+00 8.7950901e-21 +5.3824583e+00 8.5705610e-21 +5.3948703e+00 8.3520745e-21 +5.4073109e+00 8.1394590e-21 +5.4197802e+00 7.9325485e-21 +5.4322783e+00 7.7311816e-21 +5.4448051e+00 7.5352018e-21 +5.4573609e+00 7.3444576e-21 +5.4699456e+00 7.1588016e-21 +5.4825594e+00 6.9780912e-21 +5.4952022e+00 6.8021876e-21 +5.5078742e+00 6.6309565e-21 +5.5205754e+00 6.4642675e-21 +5.5333058e+00 6.3019938e-21 +5.5460657e+00 6.1440126e-21 +5.5588550e+00 5.9902048e-21 +5.5716737e+00 5.8404544e-21 +5.5845220e+00 5.6946493e-21 +5.5974000e+00 5.5526803e-21 +5.6103076e+00 5.4144416e-21 +5.6232451e+00 5.2798303e-21 +5.6362123e+00 5.1487467e-21 +5.6492094e+00 5.0210940e-21 +5.6622366e+00 4.8967779e-21 +5.6752937e+00 4.7757072e-21 +5.6883810e+00 4.6577932e-21 +5.7014985e+00 4.5429498e-21 +5.7146461e+00 4.4310931e-21 +5.7278242e+00 4.3221421e-21 +5.7410326e+00 4.2160177e-21 +5.7542714e+00 4.1126432e-21 +5.7675408e+00 4.0119443e-21 +5.7808408e+00 3.9138486e-21 +5.7941715e+00 3.8182857e-21 +5.8075329e+00 3.7251875e-21 +5.8209251e+00 3.6344875e-21 +5.8343482e+00 3.5461213e-21 +5.8478023e+00 3.4600262e-21 +5.8612873e+00 3.3761414e-21 +5.8748035e+00 3.2944078e-21 +5.8883508e+00 3.2147678e-21 +5.9019294e+00 3.1371656e-21 +5.9155393e+00 3.0615470e-21 +5.9291806e+00 2.9878591e-21 +5.9428533e+00 2.9160506e-21 +5.9565576e+00 2.8460717e-21 +5.9702935e+00 2.7778740e-21 +5.9840610e+00 2.7114103e-21 +5.9978603e+00 2.6466348e-21 +6.0116914e+00 2.5835030e-21 +6.0255544e+00 2.5219715e-21 +6.0394494e+00 2.4619984e-21 +6.0533764e+00 2.4035427e-21 +6.0673355e+00 2.3465645e-21 +6.0813269e+00 2.2910252e-21 +6.0953505e+00 2.2368872e-21 +6.1094064e+00 2.1841138e-21 +6.1234947e+00 2.1326694e-21 +6.1376155e+00 2.0825195e-21 +6.1517689e+00 2.0336304e-21 +6.1659550e+00 1.9859693e-21 +6.1801737e+00 1.9395044e-21 +6.1944252e+00 1.8942047e-21 +6.2087096e+00 1.8500401e-21 +6.2230269e+00 1.8069811e-21 +6.2373773e+00 1.7649995e-21 +6.2517607e+00 1.7240673e-21 +6.2661773e+00 1.6841575e-21 +6.2806272e+00 1.6452440e-21 +6.2951104e+00 1.6073012e-21 +6.3096269e+00 1.5703041e-21 +6.3241770e+00 1.5342287e-21 +6.3387606e+00 1.4990513e-21 +6.3533778e+00 1.4647490e-21 +6.3680288e+00 1.4312996e-21 +6.3827135e+00 1.3986814e-21 +6.3974321e+00 1.3668732e-21 +6.4121846e+00 1.3358545e-21 +6.4269711e+00 1.3056053e-21 +6.4417918e+00 1.2761061e-21 +6.4566466e+00 1.2473380e-21 +6.4715357e+00 1.2192825e-21 +6.4864591e+00 1.1919217e-21 +6.5014169e+00 1.1652381e-21 +6.5164092e+00 1.1392146e-21 +6.5314361e+00 1.1138348e-21 +6.5464977e+00 1.0890824e-21 +6.5615939e+00 1.0649418e-21 +6.5767250e+00 1.0413977e-21 +6.5918910e+00 1.0184353e-21 +6.6070920e+00 9.9603993e-22 +6.6223280e+00 9.7419760e-22 +6.6375991e+00 9.5289455e-22 +6.6529055e+00 9.3211738e-22 +6.6682471e+00 9.1185306e-22 +6.6836241e+00 8.9208892e-22 +6.6990366e+00 8.7281259e-22 +6.7144846e+00 8.5401203e-22 +6.7299683e+00 8.3567552e-22 +6.7454877e+00 8.1779165e-22 +6.7610428e+00 8.0034928e-22 +6.7766338e+00 7.8333760e-22 +6.7922608e+00 7.6674604e-22 +6.8079238e+00 7.5056432e-22 +6.8236229e+00 7.3478244e-22 +6.8393582e+00 7.1939063e-22 +6.8551299e+00 7.0437939e-22 +6.8709378e+00 6.8973946e-22 +6.8867823e+00 6.7546182e-22 +6.9026632e+00 6.6153768e-22 +6.9185808e+00 6.4795846e-22 +6.9345351e+00 6.3471584e-22 +6.9505262e+00 6.2180166e-22 +6.9665542e+00 6.0920800e-22 +6.9826191e+00 5.9692715e-22 +6.9987211e+00 5.8495157e-22 +7.0148602e+00 5.7327391e-22 +7.0310365e+00 5.6188704e-22 +7.0472502e+00 5.5078399e-22 +7.0635012e+00 5.3995794e-22 +7.0797897e+00 5.2940230e-22 +7.0961157e+00 5.1911058e-22 +7.1124794e+00 5.0907651e-22 +7.1288808e+00 4.9929395e-22 +7.1453201e+00 4.8975691e-22 +7.1617972e+00 4.8045956e-22 +7.1783124e+00 4.7139621e-22 +7.1948656e+00 4.6256133e-22 +7.2114571e+00 4.5394949e-22 +7.2280867e+00 4.4555544e-22 +7.2447548e+00 4.3737402e-22 +7.2614612e+00 4.2940022e-22 +7.2782062e+00 4.2162917e-22 +7.2949898e+00 4.1405607e-22 +7.3118121e+00 4.0667630e-22 +7.3286732e+00 3.9948530e-22 +7.3455732e+00 3.9247866e-22 +7.3625121e+00 3.8565207e-22 +7.3794901e+00 3.7900131e-22 +7.3965073e+00 3.7252228e-22 +7.4135637e+00 3.6621099e-22 +7.4306594e+00 3.6006352e-22 +7.4477946e+00 3.5407607e-22 +7.4649693e+00 3.4824494e-22 +7.4821835e+00 3.4256649e-22 +7.4994375e+00 3.3703720e-22 +7.5167313e+00 3.3165362e-22 +7.5340649e+00 3.2641241e-22 +7.5514385e+00 3.2131029e-22 +7.5688522e+00 3.1634406e-22 +7.5863060e+00 3.1151063e-22 +7.6038001e+00 3.0680695e-22 +7.6213345e+00 3.0223009e-22 +7.6389094e+00 2.9777715e-22 +7.6565248e+00 2.9344535e-22 +7.6741808e+00 2.8923195e-22 +7.6918775e+00 2.8513430e-22 +7.7096150e+00 2.8114981e-22 +7.7273934e+00 2.7727596e-22 +7.7452129e+00 2.7351032e-22 +7.7630734e+00 2.6985049e-22 +7.7809751e+00 2.6629418e-22 +7.7989181e+00 2.6283913e-22 +7.8169025e+00 2.5948317e-22 +7.8349283e+00 2.5622418e-22 +7.8529957e+00 2.5306012e-22 +7.8711048e+00 2.4998899e-22 +7.8892556e+00 2.4700888e-22 +7.9074483e+00 2.4411792e-22 +7.9256829e+00 2.4131433e-22 +7.9439596e+00 2.3859636e-22 +7.9622784e+00 2.3596235e-22 +7.9806395e+00 2.3341070e-22 +7.9990429e+00 2.3093986e-22 +8.0174888e+00 2.2854836e-22 +8.0359772e+00 2.2623477e-22 +8.0545082e+00 2.2399775e-22 +8.0730819e+00 2.2183602e-22 +8.0916985e+00 2.1974835e-22 +8.1103581e+00 2.1773359e-22 +8.1290606e+00 2.1579067e-22 +8.1478063e+00 2.1391856e-22 +8.1665952e+00 2.1211633e-22 +8.1854274e+00 2.1038310e-22 +8.2043031e+00 2.0871807e-22 +8.2232223e+00 2.0712053e-22 +8.2421851e+00 2.0558983e-22 +8.2611916e+00 2.0412541e-22 +8.2802420e+00 2.0272680e-22 +8.2993363e+00 2.0139359e-22 +8.3184746e+00 2.0012549e-22 +8.3376571e+00 1.9892230e-22 +8.3568838e+00 1.9778390e-22 +8.3761548e+00 1.9671029e-22 +8.3954703e+00 1.9570156e-22 +8.4148303e+00 1.9475794e-22 +8.4342350e+00 1.9387976e-22 +8.4536844e+00 1.9306748e-22 +8.4731786e+00 1.9232170e-22 +8.4927179e+00 1.9164315e-22 +8.5123021e+00 1.9103274e-22 +8.5319316e+00 1.9049151e-22 +8.5516063e+00 1.9002070e-22 +8.5713263e+00 1.8962172e-22 +8.5910919e+00 1.8929621e-22 +8.6109030e+00 1.8904600e-22 +8.6307598e+00 1.8887318e-22 +8.6506624e+00 1.8878008e-22 +8.6706109e+00 1.8876932e-22 +8.6906054e+00 1.8884383e-22 +8.7106460e+00 1.8900688e-22 +8.7307328e+00 1.8926209e-22 +8.7508660e+00 1.8961352e-22 +8.7710455e+00 1.9006565e-22 +8.7912716e+00 1.9062347e-22 +8.8115444e+00 1.9129252e-22 +8.8318638e+00 1.9207895e-22 +8.8522302e+00 1.9298962e-22 +8.8726435e+00 1.9403214e-22 +8.8931039e+00 1.9521499e-22 +8.9136114e+00 1.9654762e-22 +8.9341663e+00 1.9804060e-22 +8.9547686e+00 1.9970575e-22 +8.9754183e+00 2.0155632e-22 +8.9961157e+00 2.0360719e-22 +9.0168608e+00 2.0587514e-22 +9.0376538e+00 2.0837909e-22 +9.0584947e+00 2.1114052e-22 +9.0793836e+00 2.1418379e-22 +9.1003208e+00 2.1753672e-22 +9.1213062e+00 2.2123115e-22 +9.1423400e+00 2.2530370e-22 +9.1634223e+00 2.2979666e-22 +9.1845532e+00 2.3475914e-22 +9.2057328e+00 2.4024845e-22 +9.2269613e+00 2.4633187e-22 +9.2482388e+00 2.5308892e-22 +9.2695653e+00 2.6061422e-22 +9.2909410e+00 2.6902127e-22 +9.3123660e+00 2.7844732e-22 +9.3338403e+00 2.8905996e-22 +9.3553642e+00 3.0106595e-22 +9.3769378e+00 3.1472332e-22 +9.3985611e+00 3.3035836e-22 +9.4202342e+00 3.4838967e-22 +9.4419574e+00 3.6936338e-22 +9.4637306e+00 3.9400560e-22 +9.4855540e+00 4.2330328e-22 +9.5074278e+00 4.5863286e-22 +9.5293520e+00 5.0197343e-22 +9.5513267e+00 5.5627717e-22 +9.5733522e+00 6.2615122e-22 +9.5954284e+00 7.1920653e-22 +9.6175555e+00 8.4898287e-22 +9.6397337e+00 1.0421184e-21 +9.6619630e+00 1.3592421e-21 +9.6842435e+00 1.9745894e-21 +9.7065755e+00 3.6766059e-21 +9.7289589e+00 3.0667957e-20 +9.7513940e+00 4.7290231e-21 +9.7738808e+00 2.1742133e-21 +9.7964194e+00 1.4034451e-21 +9.8190100e+00 1.0320527e-21 +9.8416527e+00 8.1376875e-22 +9.8643477e+00 6.7027404e-22 +9.8870949e+00 5.6888094e-22 +9.9098946e+00 4.9352526e-22 +9.9327469e+00 4.3539072e-22 +9.9556519e+00 3.8923555e-22 +9.9786097e+00 3.5174755e-22 +1.0001620e+01 3.2073099e-22 +1.0024684e+01 2.9467291e-22 +1.0047801e+01 2.7249669e-22 +1.0070972e+01 2.5341522e-22 +1.0094195e+01 2.3683976e-22 +1.0117473e+01 2.2232120e-22 +1.0140804e+01 2.0951093e-22 +1.0164188e+01 1.9813426e-22 +1.0187627e+01 1.8797178e-22 +1.0211120e+01 1.7884616e-22 +1.0234667e+01 1.7061252e-22 +1.0258268e+01 1.6315132e-22 +1.0281924e+01 1.5636309e-22 +1.0305634e+01 1.5016438e-22 +1.0329399e+01 1.4448469e-22 +1.0353218e+01 1.3926402e-22 +1.0377093e+01 1.3445104e-22 +1.0401023e+01 1.3000155e-22 +1.0425007e+01 1.2587733e-22 +1.0449048e+01 1.2204514e-22 +1.0473143e+01 1.1847596e-22 +1.0497294e+01 1.1514432e-22 +1.0521501e+01 1.1202783e-22 +1.0545764e+01 1.0910670e-22 +1.0570082e+01 1.0636336e-22 +1.0594457e+01 1.0378224e-22 +1.0618888e+01 1.0134940e-22 +1.0643375e+01 9.9052396e-23 +1.0667919e+01 9.6880081e-23 +1.0692519e+01 9.4822417e-23 +1.0717176e+01 9.2870368e-23 +1.0741890e+01 9.1015772e-23 +1.0766661e+01 8.9251247e-23 +1.0791489e+01 8.7570098e-23 +1.0816374e+01 8.5966244e-23 +1.0841317e+01 8.4434152e-23 +1.0866317e+01 8.2968781e-23 +1.0891375e+01 8.1565526e-23 +1.0916491e+01 8.0220178e-23 +1.0941664e+01 7.8928883e-23 +1.0966896e+01 7.7688105e-23 +1.0992185e+01 7.6494599e-23 +1.1017533e+01 7.5345382e-23 +1.1042940e+01 7.4237707e-23 +1.1068405e+01 7.3169040e-23 +1.1093929e+01 7.2137047e-23 +1.1119512e+01 7.1139569e-23 +1.1145153e+01 7.0174609e-23 +1.1170854e+01 6.9240319e-23 +1.1196614e+01 6.8334988e-23 +1.1222434e+01 6.7457026e-23 +1.1248313e+01 6.6604958e-23 +1.1274251e+01 6.5777412e-23 +1.1300250e+01 6.4973114e-23 +1.1326308e+01 6.4190875e-23 +1.1352427e+01 6.3429588e-23 +1.1378606e+01 6.2688220e-23 +1.1404845e+01 6.1965808e-23 +1.1431144e+01 6.1261451e-23 +1.1457505e+01 6.0574306e-23 +1.1483926e+01 5.9903586e-23 +1.1510408e+01 5.9248551e-23 +1.1536951e+01 5.8608510e-23 +1.1563555e+01 5.7982814e-23 +1.1590221e+01 5.7370852e-23 +1.1616948e+01 5.6772052e-23 +1.1643737e+01 5.6185876e-23 +1.1670587e+01 5.5611818e-23 +1.1697500e+01 5.5049399e-23 +1.1724474e+01 5.4498170e-23 +1.1751511e+01 5.3957708e-23 +1.1778610e+01 5.3427612e-23 +1.1805772e+01 5.2907504e-23 +1.1832996e+01 5.2397027e-23 +1.1860283e+01 5.1895844e-23 +1.1887633e+01 5.1403634e-23 +1.1915046e+01 5.0920095e-23 +1.1942522e+01 5.0444939e-23 +1.1970061e+01 4.9977895e-23 +1.1997664e+01 4.9518704e-23 +1.2025331e+01 4.9067120e-23 +1.2053062e+01 4.8622910e-23 +1.2080856e+01 4.8185852e-23 +1.2108715e+01 4.7755734e-23 +1.2136637e+01 4.7332356e-23 +1.2164625e+01 4.6915526e-23 +1.2192676e+01 4.6505059e-23 +1.2220793e+01 4.6100782e-23 +1.2248974e+01 4.5702528e-23 +1.2277220e+01 4.5310136e-23 +1.2305532e+01 4.4923455e-23 +1.2333908e+01 4.4542337e-23 +1.2362350e+01 4.4166644e-23 +1.2390858e+01 4.3796239e-23 +1.2419431e+01 4.3430996e-23 +1.2448071e+01 4.3070790e-23 +1.2476776e+01 4.2715503e-23 +1.2505548e+01 4.2365020e-23 +1.2534386e+01 4.2019231e-23 +1.2563290e+01 4.1678031e-23 +1.2592261e+01 4.1341318e-23 +1.2621299e+01 4.1008994e-23 +1.2650404e+01 4.0680965e-23 +1.2679576e+01 4.0357139e-23 +1.2708815e+01 4.0037428e-23 +1.2738121e+01 3.9721747e-23 +1.2767496e+01 3.9410014e-23 +1.2796938e+01 3.9102149e-23 +1.2826447e+01 3.8798075e-23 +1.2856025e+01 3.8497718e-23 +1.2885671e+01 3.8201006e-23 +1.2915386e+01 3.7907869e-23 +1.2945169e+01 3.7618238e-23 +1.2975020e+01 3.7332048e-23 +1.3004941e+01 3.7049236e-23 +1.3034930e+01 3.6769738e-23 +1.3064989e+01 3.6493496e-23 +1.3095117e+01 3.6220450e-23 +1.3125314e+01 3.5950543e-23 +1.3155582e+01 3.5683720e-23 +1.3185918e+01 3.5419928e-23 +1.3216325e+01 3.5159113e-23 +1.3246802e+01 3.4901224e-23 +1.3277349e+01 3.4646212e-23 +1.3307967e+01 3.4394028e-23 +1.3338655e+01 3.4144625e-23 +1.3369414e+01 3.3897957e-23 +1.3400244e+01 3.3653978e-23 +1.3431145e+01 3.3412645e-23 +1.3462118e+01 3.3173915e-23 +1.3493162e+01 3.2937745e-23 +1.3524277e+01 3.2704096e-23 +1.3555464e+01 3.2472926e-23 +1.3586723e+01 3.2244198e-23 +1.3618054e+01 3.2017872e-23 +1.3649457e+01 3.1793911e-23 +1.3680933e+01 3.1572279e-23 +1.3712481e+01 3.1352940e-23 +1.3744103e+01 3.1135859e-23 +1.3775797e+01 3.0921003e-23 +1.3807564e+01 3.0708336e-23 +1.3839404e+01 3.0497827e-23 +1.3871318e+01 3.0289444e-23 +1.3903305e+01 3.0083154e-23 +1.3935366e+01 2.9878928e-23 +1.3967501e+01 2.9676735e-23 +1.3999710e+01 2.9476545e-23 +1.4031994e+01 2.9278330e-23 +1.4064352e+01 2.9082061e-23 +1.4096784e+01 2.8887710e-23 +1.4129292e+01 2.8695250e-23 +1.4161874e+01 2.8504654e-23 +1.4194531e+01 2.8315895e-23 +1.4227264e+01 2.8128949e-23 +1.4260072e+01 2.7943790e-23 +1.4292956e+01 2.7760393e-23 +1.4325916e+01 2.7578733e-23 +1.4358951e+01 2.7398788e-23 +1.4392063e+01 2.7220532e-23 +1.4425251e+01 2.7043944e-23 +1.4458516e+01 2.6869001e-23 +1.4491858e+01 2.6695681e-23 +1.4525276e+01 2.6523962e-23 +1.4558771e+01 2.6353822e-23 +1.4592344e+01 2.6185241e-23 +1.4625994e+01 2.6018197e-23 +1.4659722e+01 2.5852672e-23 +1.4693527e+01 2.5688645e-23 +1.4727410e+01 2.5526096e-23 +1.4761372e+01 2.5365006e-23 +1.4795412e+01 2.5205356e-23 +1.4829530e+01 2.5047129e-23 +1.4863727e+01 2.4890305e-23 +1.4898003e+01 2.4734867e-23 +1.4932358e+01 2.4580797e-23 +1.4966792e+01 2.4428079e-23 +1.5001306e+01 2.4276694e-23 +1.5035899e+01 2.4126627e-23 +1.5070572e+01 2.3977861e-23 +1.5105324e+01 2.3830380e-23 +1.5140157e+01 2.3684168e-23 +1.5175071e+01 2.3539209e-23 +1.5210065e+01 2.3395489e-23 +1.5245139e+01 2.3252992e-23 +1.5280295e+01 2.3111703e-23 +1.5315531e+01 2.2971607e-23 +1.5350849e+01 2.2832692e-23 +1.5386248e+01 2.2694941e-23 +1.5421729e+01 2.2558342e-23 +1.5457291e+01 2.2422881e-23 +1.5492936e+01 2.2288545e-23 +1.5528663e+01 2.2155320e-23 +1.5564472e+01 2.2023193e-23 +1.5600364e+01 2.1892151e-23 +1.5636338e+01 2.1762183e-23 +1.5672396e+01 2.1633275e-23 +1.5708537e+01 2.1505416e-23 +1.5744761e+01 2.1378593e-23 +1.5781068e+01 2.1252794e-23 +1.5817459e+01 2.1128009e-23 +1.5853935e+01 2.1004225e-23 +1.5890494e+01 2.0881432e-23 +1.5927137e+01 2.0759618e-23 +1.5963866e+01 2.0638773e-23 +1.6000678e+01 2.0518885e-23 +1.6037576e+01 2.0399945e-23 +1.6074559e+01 2.0281941e-23 +1.6111627e+01 2.0164864e-23 +1.6148780e+01 2.0048703e-23 +1.6186020e+01 1.9933448e-23 +1.6223345e+01 1.9819091e-23 +1.6260756e+01 1.9705620e-23 +1.6298253e+01 1.9593027e-23 +1.6335837e+01 1.9481302e-23 +1.6373508e+01 1.9370436e-23 +1.6411265e+01 1.9260420e-23 +1.6449110e+01 1.9151245e-23 +1.6487041e+01 1.9042902e-23 +1.6525061e+01 1.8935382e-23 +1.6563168e+01 1.8828677e-23 +1.6601362e+01 1.8722779e-23 +1.6639645e+01 1.8617679e-23 +1.6678016e+01 1.8513369e-23 +1.6716476e+01 1.8409840e-23 +1.6755024e+01 1.8307086e-23 +1.6793662e+01 1.8205098e-23 +1.6832388e+01 1.8103867e-23 +1.6871203e+01 1.8003388e-23 +1.6910109e+01 1.7903652e-23 +1.6949103e+01 1.7804651e-23 +1.6988188e+01 1.7706379e-23 +1.7027363e+01 1.7608828e-23 +1.7066628e+01 1.7511991e-23 +1.7105984e+01 1.7415861e-23 +1.7145431e+01 1.7320431e-23 +1.7184968e+01 1.7225694e-23 +1.7224597e+01 1.7131644e-23 +1.7264317e+01 1.7038274e-23 +1.7304128e+01 1.6945577e-23 +1.7344032e+01 1.6853547e-23 +1.7384027e+01 1.6762177e-23 +1.7424115e+01 1.6671462e-23 +1.7464295e+01 1.6581395e-23 +1.7504568e+01 1.6491969e-23 +1.7544934e+01 1.6403179e-23 +1.7585392e+01 1.6315019e-23 +1.7625945e+01 1.6227483e-23 +1.7666590e+01 1.6140565e-23 +1.7707329e+01 1.6054259e-23 +1.7748163e+01 1.5968560e-23 +1.7789090e+01 1.5883462e-23 +1.7830112e+01 1.5798960e-23 +1.7871228e+01 1.5715048e-23 +1.7912439e+01 1.5631720e-23 +1.7953746e+01 1.5548971e-23 +1.7995147e+01 1.5466797e-23 +1.8036644e+01 1.5385192e-23 +1.8078237e+01 1.5304150e-23 +1.8119925e+01 1.5223667e-23 +1.8161710e+01 1.5143738e-23 +1.8203591e+01 1.5064357e-23 +1.8245569e+01 1.4985521e-23 +1.8287643e+01 1.4907223e-23 +1.8329814e+01 1.4829460e-23 +1.8372083e+01 1.4752226e-23 +1.8414449e+01 1.4675518e-23 +1.8456913e+01 1.4599329e-23 +1.8499475e+01 1.4523657e-23 +1.8542135e+01 1.4448496e-23 +1.8584893e+01 1.4373841e-23 +1.8627750e+01 1.4299689e-23 +1.8670706e+01 1.4226036e-23 +1.8713761e+01 1.4152876e-23 +1.8756915e+01 1.4080206e-23 +1.8800168e+01 1.4008021e-23 +1.8843522e+01 1.3936318e-23 +1.8886975e+01 1.3865092e-23 +1.8930528e+01 1.3794339e-23 +1.8974182e+01 1.3724056e-23 +1.9017937e+01 1.3654238e-23 +1.9061792e+01 1.3584881e-23 +1.9105749e+01 1.3515982e-23 +1.9149807e+01 1.3447537e-23 +1.9193967e+01 1.3379541e-23 +1.9238228e+01 1.3311992e-23 +1.9282592e+01 1.3244885e-23 +1.9327057e+01 1.3178217e-23 +1.9371626e+01 1.3111985e-23 +1.9416297e+01 1.3046184e-23 +1.9461071e+01 1.2980812e-23 +1.9505948e+01 1.2915864e-23 +1.9550929e+01 1.2851337e-23 +1.9596014e+01 1.2787229e-23 +1.9641202e+01 1.2723534e-23 +1.9686495e+01 1.2660251e-23 +1.9731892e+01 1.2597376e-23 +1.9777394e+01 1.2534905e-23 +1.9823001e+01 1.2472836e-23 +1.9868713e+01 1.2411164e-23 +1.9914530e+01 1.2349888e-23 +1.9960454e+01 1.2289003e-23 +2.0006482e+01 1.2228508e-23 +2.0052618e+01 1.2168398e-23 +2.0098859e+01 1.2108670e-23 +2.0145207e+01 1.2049322e-23 +2.0191662e+01 1.1990351e-23 +2.0238224e+01 1.1931753e-23 +2.0284894e+01 1.1873526e-23 +2.0331671e+01 1.1815668e-23 +2.0378556e+01 1.1758174e-23 +2.0425549e+01 1.1701042e-23 +2.0472650e+01 1.1644270e-23 +2.0519861e+01 1.1587855e-23 +2.0567180e+01 1.1531793e-23 +2.0614608e+01 1.1476083e-23 +2.0662145e+01 1.1420721e-23 +2.0709792e+01 1.1365705e-23 +2.0757549e+01 1.1311032e-23 +2.0805416e+01 1.1256700e-23 +2.0853394e+01 1.1202707e-23 +2.0901482e+01 1.1149048e-23 +2.0949681e+01 1.1095723e-23 +2.0997991e+01 1.1042728e-23 +2.1046412e+01 1.0990061e-23 +2.1094945e+01 1.0937720e-23 +2.1143591e+01 1.0885702e-23 +2.1192348e+01 1.0834004e-23 +2.1241218e+01 1.0782625e-23 +2.1290200e+01 1.0731562e-23 +2.1339295e+01 1.0680812e-23 +2.1388504e+01 1.0630374e-23 +2.1437826e+01 1.0580245e-23 +2.1487262e+01 1.0530423e-23 +2.1536812e+01 1.0480905e-23 +2.1586476e+01 1.0431690e-23 +2.1636254e+01 1.0382774e-23 +2.1686148e+01 1.0334157e-23 +2.1736156e+01 1.0285835e-23 +2.1786280e+01 1.0237807e-23 +2.1836519e+01 1.0190070e-23 +2.1886874e+01 1.0142623e-23 +2.1937346e+01 1.0095463e-23 +2.1987933e+01 1.0048588e-23 +2.2038638e+01 1.0001997e-23 +2.2089459e+01 9.9556865e-24 +2.2140397e+01 9.9096553e-24 +2.2191453e+01 9.8639012e-24 +2.2242627e+01 9.8184223e-24 +2.2293919e+01 9.7732167e-24 +2.2345328e+01 9.7282823e-24 +2.2396857e+01 9.6836173e-24 +2.2448504e+01 9.6392199e-24 +2.2500271e+01 9.5950880e-24 +2.2552157e+01 9.5512198e-24 +2.2604162e+01 9.5076136e-24 +2.2656287e+01 9.4642674e-24 +2.2708533e+01 9.4211794e-24 +2.2760899e+01 9.3783479e-24 +2.2813386e+01 9.3357711e-24 +2.2865994e+01 9.2934471e-24 +2.2918723e+01 9.2513742e-24 +2.2971574e+01 9.2095508e-24 +2.3024546e+01 9.1679751e-24 +2.3077641e+01 9.1266453e-24 +2.3130858e+01 9.0855598e-24 +2.3184198e+01 9.0447169e-24 +2.3237661e+01 9.0041150e-24 +2.3291247e+01 8.9637524e-24 +2.3344957e+01 8.9236274e-24 +2.3398791e+01 8.8837385e-24 +2.3452748e+01 8.8440841e-24 +2.3506831e+01 8.8046625e-24 +2.3561038e+01 8.7654722e-24 +2.3615370e+01 8.7265116e-24 +2.3669827e+01 8.6877791e-24 +2.3724410e+01 8.6492734e-24 +2.3779118e+01 8.6109927e-24 +2.3833953e+01 8.5729356e-24 +2.3888914e+01 8.5351006e-24 +2.3944002e+01 8.4974863e-24 +2.3999217e+01 8.4600911e-24 +2.4054560e+01 8.4229135e-24 +2.4110030e+01 8.3859522e-24 +2.4165628e+01 8.3492058e-24 +2.4221354e+01 8.3126727e-24 +2.4277208e+01 8.2763516e-24 +2.4333192e+01 8.2402410e-24 +2.4389304e+01 8.2043397e-24 +2.4445546e+01 8.1686461e-24 +2.4501918e+01 8.1331590e-24 +2.4558420e+01 8.0978770e-24 +2.4615051e+01 8.0627987e-24 +2.4671814e+01 8.0279229e-24 +2.4728707e+01 7.9932481e-24 +2.4785732e+01 7.9587731e-24 +2.4842888e+01 7.9244967e-24 +2.4900176e+01 7.8904174e-24 +2.4957596e+01 7.8565340e-24 +2.5015148e+01 7.8228453e-24 +2.5072833e+01 7.7893500e-24 +2.5130652e+01 7.7560469e-24 +2.5188603e+01 7.7229347e-24 +2.5246688e+01 7.6900121e-24 +2.5304907e+01 7.6572780e-24 +2.5363261e+01 7.6247312e-24 +2.5421748e+01 7.5923705e-24 +2.5480371e+01 7.5601946e-24 +2.5539129e+01 7.5282024e-24 +2.5598023e+01 7.4963928e-24 +2.5657052e+01 7.4647645e-24 +2.5716217e+01 7.4333164e-24 +2.5775519e+01 7.4020475e-24 +2.5834957e+01 7.3709564e-24 +2.5894533e+01 7.3400422e-24 +2.5954246e+01 7.3093036e-24 +2.6014097e+01 7.2787397e-24 +2.6074085e+01 7.2483493e-24 +2.6134212e+01 7.2181312e-24 +2.6194478e+01 7.1880845e-24 +2.6254883e+01 7.1582080e-24 +2.6315427e+01 7.1285008e-24 +2.6376110e+01 7.0989616e-24 +2.6436934e+01 7.0695896e-24 +2.6497898e+01 7.0403835e-24 +2.6559002e+01 7.0113425e-24 +2.6620247e+01 6.9824655e-24 +2.6681634e+01 6.9537514e-24 +2.6743162e+01 6.9251993e-24 +2.6804832e+01 6.8968081e-24 +2.6866644e+01 6.8685770e-24 +2.6928599e+01 6.8405047e-24 +2.6990696e+01 6.8125905e-24 +2.7052937e+01 6.7848333e-24 +2.7115321e+01 6.7572322e-24 +2.7177849e+01 6.7297861e-24 +2.7240522e+01 6.7024942e-24 +2.7303338e+01 6.6753555e-24 +2.7366300e+01 6.6483691e-24 +2.7429407e+01 6.6215340e-24 +2.7492659e+01 6.5948494e-24 +2.7556058e+01 6.5683142e-24 +2.7619602e+01 6.5419276e-24 +2.7683293e+01 6.5156888e-24 +2.7747131e+01 6.4895967e-24 +2.7811116e+01 6.4636505e-24 +2.7875249e+01 6.4378493e-24 +2.7939529e+01 6.4121923e-24 +2.8003958e+01 6.3866786e-24 +2.8068536e+01 6.3613073e-24 +2.8133262e+01 6.3360775e-24 +2.8198137e+01 6.3109884e-24 +2.8263162e+01 6.2860392e-24 +2.8328338e+01 6.2612290e-24 +2.8393663e+01 6.2365569e-24 +2.8459139e+01 6.2120223e-24 +2.8524766e+01 6.1876241e-24 +2.8590544e+01 6.1633617e-24 +2.8656474e+01 6.1392341e-24 +2.8722556e+01 6.1152407e-24 +2.8788791e+01 6.0913806e-24 +2.8855178e+01 6.0676530e-24 +2.8921718e+01 6.0440571e-24 +2.8988412e+01 6.0205921e-24 +2.9055259e+01 5.9972573e-24 +2.9122261e+01 5.9740519e-24 +2.9189417e+01 5.9509751e-24 +2.9256728e+01 5.9280261e-24 +2.9324194e+01 5.9052043e-24 +2.9391816e+01 5.8825088e-24 +2.9459594e+01 5.8599389e-24 +2.9527528e+01 5.8374939e-24 +2.9595619e+01 5.8151731e-24 +2.9663866e+01 5.7929756e-24 +2.9732272e+01 5.7709008e-24 +2.9800834e+01 5.7489480e-24 +2.9869555e+01 5.7271164e-24 +2.9938435e+01 5.7054053e-24 +3.0007473e+01 5.6838140e-24 +3.0076670e+01 5.6623419e-24 +3.0146028e+01 5.6409882e-24 +3.0215544e+01 5.6197523e-24 +3.0285222e+01 5.5986334e-24 +3.0355060e+01 5.5776308e-24 +3.0425059e+01 5.5567439e-24 +3.0495219e+01 5.5359721e-24 +3.0565541e+01 5.5153146e-24 +3.0636026e+01 5.4947708e-24 +3.0706673e+01 5.4743399e-24 +3.0777482e+01 5.4540215e-24 +3.0848456e+01 5.4338148e-24 +3.0919592e+01 5.4137191e-24 +3.0990893e+01 5.3937338e-24 +3.1062358e+01 5.3738583e-24 +3.1133988e+01 5.3540920e-24 +3.1205784e+01 5.3344342e-24 +3.1277744e+01 5.3148842e-24 +3.1349871e+01 5.2954416e-24 +3.1422164e+01 5.2761055e-24 +3.1494624e+01 5.2568755e-24 +3.1567251e+01 5.2377509e-24 +3.1640045e+01 5.2187312e-24 +3.1713007e+01 5.1998156e-24 +3.1786138e+01 5.1810037e-24 +3.1859437e+01 5.1622947e-24 +3.1932905e+01 5.1436882e-24 +3.2006542e+01 5.1251835e-24 +3.2080350e+01 5.1067801e-24 +3.2154327e+01 5.0884773e-24 +3.2228475e+01 5.0702747e-24 +3.2302794e+01 5.0521715e-24 +3.2377285e+01 5.0341673e-24 +3.2451947e+01 5.0162615e-24 +3.2526782e+01 4.9984535e-24 +3.2601789e+01 4.9807428e-24 +3.2676969e+01 4.9631288e-24 +3.2752322e+01 4.9456109e-24 +3.2827849e+01 4.9281886e-24 +3.2903550e+01 4.9108614e-24 +3.2979426e+01 4.8936287e-24 +3.3055477e+01 4.8764900e-24 +3.3131703e+01 4.8594448e-24 +3.3208105e+01 4.8424924e-24 +3.3284683e+01 4.8256324e-24 +3.3361438e+01 4.8088643e-24 +3.3438370e+01 4.7921875e-24 +3.3515479e+01 4.7756016e-24 +3.3592766e+01 4.7591059e-24 +3.3670231e+01 4.7427000e-24 +3.3747875e+01 4.7263834e-24 +3.3825698e+01 4.7101556e-24 +3.3903700e+01 4.6940160e-24 +3.3981882e+01 4.6779642e-24 +3.4060245e+01 4.6619997e-24 +3.4138788e+01 4.6461219e-24 +3.4217512e+01 4.6303304e-24 +3.4296418e+01 4.6146247e-24 +3.4375506e+01 4.5990044e-24 +3.4454776e+01 4.5834688e-24 +3.4534229e+01 4.5680176e-24 +3.4613865e+01 4.5526502e-24 +3.4693685e+01 4.5373663e-24 +3.4773689e+01 4.5221652e-24 +3.4853877e+01 4.5070466e-24 +3.4934251e+01 4.4920100e-24 +3.5014809e+01 4.4770550e-24 +3.5095554e+01 4.4621809e-24 +3.5176484e+01 4.4473875e-24 +3.5257602e+01 4.4326742e-24 +3.5338906e+01 4.4180407e-24 +3.5420398e+01 4.4034863e-24 +3.5502077e+01 4.3890108e-24 +3.5583945e+01 4.3746136e-24 +3.5666002e+01 4.3602944e-24 +3.5748248e+01 4.3460526e-24 +3.5830684e+01 4.3318878e-24 +3.5913310e+01 4.3177996e-24 +3.5996126e+01 4.3037876e-24 +3.6079134e+01 4.2898514e-24 +3.6162332e+01 4.2759904e-24 +3.6245723e+01 4.2622044e-24 +3.6329306e+01 4.2484928e-24 +3.6413082e+01 4.2348552e-24 +3.6497050e+01 4.2212913e-24 +3.6581213e+01 4.2078006e-24 +3.6665569e+01 4.1943827e-24 +3.6750120e+01 4.1810372e-24 +3.6834867e+01 4.1677637e-24 +3.6919808e+01 4.1545617e-24 +3.7004945e+01 4.1414310e-24 +3.7090279e+01 4.1283710e-24 +3.7175810e+01 4.1153813e-24 +3.7261537e+01 4.1024617e-24 +3.7347463e+01 4.0896116e-24 +3.7433586e+01 4.0768307e-24 +3.7519908e+01 4.0641187e-24 +3.7606429e+01 4.0514750e-24 +3.7693150e+01 4.0388994e-24 +3.7780071e+01 4.0263914e-24 +3.7867192e+01 4.0139507e-24 +3.7954514e+01 4.0015769e-24 +3.8042037e+01 3.9892696e-24 +3.8129763e+01 3.9770284e-24 +3.8217690e+01 3.9648530e-24 +3.8305821e+01 3.9527430e-24 +3.8394154e+01 3.9406980e-24 +3.8482691e+01 3.9287177e-24 +3.8571433e+01 3.9168017e-24 +3.8660379e+01 3.9049496e-24 +3.8749530e+01 3.8931611e-24 +3.8838886e+01 3.8814358e-24 +3.8928449e+01 3.8697733e-24 +3.9018219e+01 3.8581734e-24 +3.9108195e+01 3.8466356e-24 +3.9198379e+01 3.8351596e-24 +3.9288770e+01 3.8237451e-24 +3.9379371e+01 3.8123917e-24 +3.9470180e+01 3.8010991e-24 +3.9561198e+01 3.7898668e-24 +3.9652427e+01 3.7786947e-24 +3.9743865e+01 3.7675823e-24 +3.9835515e+01 3.7565293e-24 +3.9927376e+01 3.7455354e-24 +4.0019449e+01 3.7346002e-24 +4.0111734e+01 3.7237235e-24 +4.0204232e+01 3.7129048e-24 +4.0296943e+01 3.7021438e-24 +4.0389868e+01 3.6914403e-24 +4.0483008e+01 3.6807939e-24 +4.0576362e+01 3.6702043e-24 +4.0669931e+01 3.6596711e-24 +4.0763716e+01 3.6491941e-24 +4.0857718e+01 3.6387729e-24 +4.0951936e+01 3.6284072e-24 +4.1046372e+01 3.6180967e-24 +4.1141025e+01 3.6078411e-24 +4.1235896e+01 3.5976400e-24 +4.1330987e+01 3.5874933e-24 +4.1426296e+01 3.5774005e-24 +4.1521826e+01 3.5673613e-24 +4.1617575e+01 3.5573755e-24 +4.1713546e+01 3.5474428e-24 +4.1809737e+01 3.5375628e-24 +4.1906151e+01 3.5277352e-24 +4.2002787e+01 3.5179598e-24 +4.2099646e+01 3.5082363e-24 +4.2196728e+01 3.4985644e-24 +4.2294034e+01 3.4889437e-24 +4.2391564e+01 3.4793740e-24 +4.2489319e+01 3.4698550e-24 +4.2587300e+01 3.4603864e-24 +4.2685507e+01 3.4509680e-24 +4.2783940e+01 3.4415994e-24 +4.2882600e+01 3.4322803e-24 +4.2981488e+01 3.4230106e-24 +4.3080603e+01 3.4137898e-24 +4.3179947e+01 3.4046178e-24 +4.3279521e+01 3.3954942e-24 +4.3379324e+01 3.3864188e-24 +4.3479357e+01 3.3773913e-24 +4.3579620e+01 3.3684115e-24 +4.3680115e+01 3.3594790e-24 +4.3780842e+01 3.3505935e-24 +4.3881801e+01 3.3417550e-24 +4.3982993e+01 3.3329629e-24 +4.4084418e+01 3.3242172e-24 +4.4186077e+01 3.3155175e-24 +4.4287970e+01 3.3068636e-24 +4.4390099e+01 3.2982552e-24 +4.4492463e+01 3.2896921e-24 +4.4595062e+01 3.2811739e-24 +4.4697899e+01 3.2727005e-24 +4.4800973e+01 3.2642716e-24 +4.4904284e+01 3.2558870e-24 +4.5007834e+01 3.2475463e-24 +4.5111622e+01 3.2392493e-24 +4.5215650e+01 3.2309959e-24 +4.5319918e+01 3.2227857e-24 +4.5424426e+01 3.2146185e-24 +4.5529175e+01 3.2064940e-24 +4.5634165e+01 3.1984120e-24 +4.5739398e+01 3.1903724e-24 +4.5844873e+01 3.1823747e-24 +4.5950592e+01 3.1744189e-24 +4.6056554e+01 3.1665046e-24 +4.6162761e+01 3.1586316e-24 +4.6269213e+01 3.1507997e-24 +4.6375910e+01 3.1430086e-24 +4.6482853e+01 3.1352582e-24 +4.6590043e+01 3.1275481e-24 +4.6697480e+01 3.1198782e-24 +4.6805165e+01 3.1122483e-24 +4.6913098e+01 3.1046580e-24 +4.7021280e+01 3.0971072e-24 +4.7129711e+01 3.0895957e-24 +4.7238393e+01 3.0821232e-24 +4.7347325e+01 3.0746894e-24 +4.7456508e+01 3.0672943e-24 +4.7565943e+01 3.0599376e-24 +4.7675631e+01 3.0526190e-24 +4.7785571e+01 3.0453383e-24 +4.7895765e+01 3.0380953e-24 +4.8006213e+01 3.0308899e-24 +4.8116916e+01 3.0237217e-24 +4.8227874e+01 3.0165906e-24 +4.8339087e+01 3.0094964e-24 +4.8450558e+01 3.0024388e-24 +4.8562285e+01 2.9954177e-24 +4.8674270e+01 2.9884328e-24 +4.8786513e+01 2.9814839e-24 +4.8899015e+01 2.9745709e-24 +4.9011777e+01 2.9676935e-24 +4.9124798e+01 2.9608515e-24 +4.9238081e+01 2.9540447e-24 +4.9351624e+01 2.9472730e-24 +4.9465429e+01 2.9405360e-24 +4.9579497e+01 2.9338337e-24 +4.9693828e+01 2.9271658e-24 +4.9808422e+01 2.9205322e-24 +4.9923280e+01 2.9139326e-24 +5.0038404e+01 2.9073668e-24 +5.0153793e+01 2.9008346e-24 +5.0269448e+01 2.8943359e-24 +5.0385370e+01 2.8878705e-24 +5.0501559e+01 2.8814382e-24 +5.0618016e+01 2.8750387e-24 +5.0734741e+01 2.8686720e-24 +5.0851736e+01 2.8623377e-24 +5.0969000e+01 2.8560358e-24 +5.1086535e+01 2.8497661e-24 +5.1204341e+01 2.8435283e-24 +5.1322419e+01 2.8373222e-24 +5.1440769e+01 2.8311478e-24 +5.1559392e+01 2.8250048e-24 +5.1678288e+01 2.8188931e-24 +5.1797458e+01 2.8128124e-24 +5.1916904e+01 2.8067626e-24 +5.2036625e+01 2.8007435e-24 +5.2156621e+01 2.7947549e-24 +5.2276895e+01 2.7887967e-24 +5.2397446e+01 2.7828687e-24 +5.2518275e+01 2.7769707e-24 +5.2639382e+01 2.7711026e-24 +5.2760769e+01 2.7652641e-24 +5.2882436e+01 2.7594552e-24 +5.3004383e+01 2.7536756e-24 +5.3126612e+01 2.7479251e-24 +5.3249122e+01 2.7422037e-24 +5.3371915e+01 2.7365111e-24 +5.3494991e+01 2.7308472e-24 +5.3618351e+01 2.7252118e-24 +5.3741995e+01 2.7196048e-24 +5.3865925e+01 2.7140260e-24 +5.3990140e+01 2.7084752e-24 +5.4114642e+01 2.7029523e-24 +5.4239430e+01 2.6974570e-24 +5.4364507e+01 2.6919894e-24 +5.4489872e+01 2.6865492e-24 +5.4615526e+01 2.6811362e-24 +5.4741470e+01 2.6757503e-24 +5.4867704e+01 2.6703913e-24 +5.4994229e+01 2.6650591e-24 +5.5121046e+01 2.6597536e-24 +5.5248156e+01 2.6544745e-24 +5.5375559e+01 2.6492218e-24 +5.5503255e+01 2.6439952e-24 +5.5631246e+01 2.6387947e-24 +5.5759532e+01 2.6336201e-24 +5.5888114e+01 2.6284712e-24 +5.6016992e+01 2.6233480e-24 +5.6146168e+01 2.6182501e-24 +5.6275642e+01 2.6131776e-24 +5.6405414e+01 2.6081302e-24 +5.6535485e+01 2.6031079e-24 +5.6665856e+01 2.5981104e-24 +5.6796528e+01 2.5931377e-24 +5.6927501e+01 2.5881895e-24 +5.7058777e+01 2.5832659e-24 +5.7190355e+01 2.5783665e-24 +5.7322236e+01 2.5734913e-24 +5.7454421e+01 2.5686402e-24 +5.7586912e+01 2.5638130e-24 +5.7719708e+01 2.5590095e-24 +5.7852810e+01 2.5542297e-24 +5.7986219e+01 2.5494734e-24 +5.8119935e+01 2.5447405e-24 +5.8253960e+01 2.5400308e-24 +5.8388294e+01 2.5353442e-24 +5.8522938e+01 2.5306806e-24 +5.8657893e+01 2.5260399e-24 +5.8793158e+01 2.5214219e-24 +5.8928736e+01 2.5168264e-24 +5.9064626e+01 2.5122534e-24 +5.9200829e+01 2.5077028e-24 +5.9337347e+01 2.5031744e-24 +5.9474179e+01 2.4986680e-24 +5.9611327e+01 2.4941836e-24 +5.9748791e+01 2.4897211e-24 +5.9886572e+01 2.4852803e-24 +6.0024671e+01 2.4808610e-24 +6.0163089e+01 2.4764633e-24 +6.0301825e+01 2.4720868e-24 +6.0440882e+01 2.4677316e-24 +6.0580259e+01 2.4633976e-24 +6.0719957e+01 2.4590845e-24 +6.0859978e+01 2.4547922e-24 +6.1000322e+01 2.4505208e-24 +6.1140989e+01 2.4462699e-24 +6.1281980e+01 2.4420396e-24 +6.1423297e+01 2.4378297e-24 +6.1564940e+01 2.4336401e-24 +6.1706909e+01 2.4294707e-24 +6.1849206e+01 2.4253213e-24 +6.1991830e+01 2.4211918e-24 +6.2134784e+01 2.4170822e-24 +6.2278067e+01 2.4129924e-24 +6.2421681e+01 2.4089221e-24 +6.2565626e+01 2.4048714e-24 +6.2709903e+01 2.4008400e-24 +6.2854512e+01 2.3968279e-24 +6.2999455e+01 2.3928350e-24 +6.3144732e+01 2.3888612e-24 +6.3290345e+01 2.3849063e-24 +6.3436293e+01 2.3809703e-24 +6.3582577e+01 2.3770531e-24 +6.3729199e+01 2.3731545e-24 +6.3876159e+01 2.3692744e-24 +6.4023458e+01 2.3654127e-24 +6.4171097e+01 2.3615694e-24 +6.4319076e+01 2.3577443e-24 +6.4467396e+01 2.3539374e-24 +6.4616058e+01 2.3501484e-24 +6.4765063e+01 2.3463774e-24 +6.4914412e+01 2.3426242e-24 +6.5064105e+01 2.3388887e-24 +6.5214144e+01 2.3351709e-24 +6.5364528e+01 2.3314706e-24 +6.5515259e+01 2.3277876e-24 +6.5666338e+01 2.3241221e-24 +6.5817765e+01 2.3204737e-24 +6.5969541e+01 2.3168425e-24 +6.6121667e+01 2.3132283e-24 +6.6274144e+01 2.3096311e-24 +6.6426973e+01 2.3060507e-24 +6.6580154e+01 2.3024871e-24 +6.6733688e+01 2.2989401e-24 +6.6887577e+01 2.2954097e-24 +6.7041820e+01 2.2918957e-24 +6.7196419e+01 2.2883982e-24 +6.7351374e+01 2.2849169e-24 +6.7506687e+01 2.2814518e-24 +6.7662358e+01 2.2780028e-24 +6.7818388e+01 2.2745698e-24 +6.7974778e+01 2.2711528e-24 +6.8131528e+01 2.2677516e-24 +6.8288640e+01 2.2643661e-24 +6.8446114e+01 2.2609963e-24 +6.8603951e+01 2.2576421e-24 +6.8762153e+01 2.2543034e-24 +6.8920719e+01 2.2509800e-24 +6.9079650e+01 2.2476720e-24 +6.9238949e+01 2.2443791e-24 +6.9398614e+01 2.2411015e-24 +6.9558648e+01 2.2378388e-24 +6.9719051e+01 2.2345912e-24 +6.9879823e+01 2.2313584e-24 +7.0040967e+01 2.2281404e-24 +7.0202482e+01 2.2249372e-24 +7.0364369e+01 2.2217485e-24 +7.0526630e+01 2.2185745e-24 +7.0689265e+01 2.2154149e-24 +7.0852275e+01 2.2122697e-24 +7.1015661e+01 2.2091388e-24 +7.1179423e+01 2.2060222e-24 +7.1343564e+01 2.2029196e-24 +7.1508083e+01 2.1998312e-24 +7.1672981e+01 2.1967568e-24 +7.1838259e+01 2.1936962e-24 +7.2003919e+01 2.1906496e-24 +7.2169960e+01 2.1876166e-24 +7.2336385e+01 2.1845974e-24 +7.2503193e+01 2.1815917e-24 +7.2670386e+01 2.1785996e-24 +7.2837964e+01 2.1756209e-24 +7.3005929e+01 2.1726557e-24 +7.3174282e+01 2.1697037e-24 +7.3343022e+01 2.1667649e-24 +7.3512152e+01 2.1638393e-24 +7.3681671e+01 2.1609268e-24 +7.3851582e+01 2.1580273e-24 +7.4021884e+01 2.1551407e-24 +7.4192579e+01 2.1522670e-24 +7.4363668e+01 2.1494061e-24 +7.4535151e+01 2.1465578e-24 +7.4707029e+01 2.1437223e-24 +7.4879304e+01 2.1408993e-24 +7.5051977e+01 2.1380888e-24 +7.5225047e+01 2.1352908e-24 +7.5398517e+01 2.1325051e-24 +7.5572386e+01 2.1297317e-24 +7.5746657e+01 2.1269705e-24 +7.5921329e+01 2.1242216e-24 +7.6096404e+01 2.1214846e-24 +7.6271883e+01 2.1187598e-24 +7.6447767e+01 2.1160468e-24 +7.6624056e+01 2.1133458e-24 +7.6800751e+01 2.1106566e-24 +7.6977855e+01 2.1079791e-24 +7.7155366e+01 2.1053134e-24 +7.7333287e+01 2.1026593e-24 +7.7511618e+01 2.1000167e-24 +7.7690361e+01 2.0973856e-24 +7.7869515e+01 2.0947660e-24 +7.8049083e+01 2.0921577e-24 +7.8229065e+01 2.0895607e-24 +7.8409461e+01 2.0869750e-24 +7.8590274e+01 2.0844004e-24 +7.8771504e+01 2.0818370e-24 +7.8953152e+01 2.0792846e-24 +7.9135218e+01 2.0767432e-24 +7.9317705e+01 2.0742128e-24 +7.9500612e+01 2.0716932e-24 +7.9683941e+01 2.0691844e-24 +7.9867693e+01 2.0666864e-24 +8.0051868e+01 2.0641990e-24 +8.0236468e+01 2.0617223e-24 +8.0421494e+01 2.0592562e-24 +8.0606947e+01 2.0568006e-24 +8.0792827e+01 2.0543554e-24 +8.0979136e+01 2.0519207e-24 +8.1165874e+01 2.0494962e-24 +8.1353044e+01 2.0470821e-24 +8.1540644e+01 2.0446781e-24 +8.1728678e+01 2.0422844e-24 +8.1917145e+01 2.0399007e-24 +8.2106046e+01 2.0375271e-24 +8.2295383e+01 2.0351635e-24 +8.2485157e+01 2.0328099e-24 +8.2675369e+01 2.0304661e-24 +8.2866019e+01 2.0281322e-24 +8.3057108e+01 2.0258080e-24 +8.3248639e+01 2.0234936e-24 +8.3440611e+01 2.0211888e-24 +8.3633025e+01 2.0188937e-24 +8.3825884e+01 2.0166081e-24 +8.4019187e+01 2.0143321e-24 +8.4212936e+01 2.0120655e-24 +8.4407131e+01 2.0098083e-24 +8.4601775e+01 2.0075604e-24 +8.4796867e+01 2.0053219e-24 +8.4992409e+01 2.0030926e-24 +8.5188403e+01 2.0008725e-24 +8.5384848e+01 1.9986616e-24 +8.5581746e+01 1.9964597e-24 +8.5779098e+01 1.9942670e-24 +8.5976905e+01 1.9920832e-24 +8.6175169e+01 1.9899084e-24 +8.6373889e+01 1.9877424e-24 +8.6573068e+01 1.9855854e-24 +8.6772706e+01 1.9834371e-24 +8.6972805e+01 1.9812976e-24 +8.7173365e+01 1.9791668e-24 +8.7374387e+01 1.9770447e-24 +8.7575873e+01 1.9749311e-24 +8.7777824e+01 1.9728262e-24 +8.7980240e+01 1.9707298e-24 +8.8183123e+01 1.9686418e-24 +8.8386474e+01 1.9665623e-24 +8.8590294e+01 1.9644912e-24 +8.8794584e+01 1.9624284e-24 +8.8999345e+01 1.9603739e-24 +8.9204578e+01 1.9583276e-24 +8.9410285e+01 1.9562896e-24 +8.9616465e+01 1.9542597e-24 +8.9823122e+01 1.9522379e-24 +9.0030254e+01 1.9502242e-24 +9.0237865e+01 1.9482185e-24 +9.0445954e+01 1.9462208e-24 +9.0654523e+01 1.9442311e-24 +9.0863573e+01 1.9422493e-24 +9.1073105e+01 1.9402753e-24 +9.1283120e+01 1.9383091e-24 +9.1493620e+01 1.9363507e-24 +9.1704605e+01 1.9344001e-24 +9.1916077e+01 1.9324571e-24 +9.2128036e+01 1.9305218e-24 +9.2340484e+01 1.9285941e-24 +9.2553422e+01 1.9266740e-24 +9.2766850e+01 1.9247614e-24 +9.2980771e+01 1.9228563e-24 +9.3195186e+01 1.9209586e-24 +9.3410095e+01 1.9190684e-24 +9.3625499e+01 1.9171855e-24 +9.3841400e+01 1.9153100e-24 +9.4057799e+01 1.9134417e-24 +9.4274697e+01 1.9115808e-24 +9.4492095e+01 1.9097270e-24 +9.4709995e+01 1.9078805e-24 +9.4928397e+01 1.9060410e-24 +9.5147302e+01 1.9042087e-24 +9.5366713e+01 1.9023835e-24 +9.5586629e+01 1.9005653e-24 +9.5807053e+01 1.8987541e-24 +9.6027984e+01 1.8969498e-24 +9.6249426e+01 1.8951525e-24 +9.6471378e+01 1.8933621e-24 +9.6693841e+01 1.8915785e-24 +9.6916818e+01 1.8898018e-24 +9.7140309e+01 1.8880318e-24 +9.7364315e+01 1.8862686e-24 +9.7588838e+01 1.8845121e-24 +9.7813879e+01 1.8827623e-24 +9.8039438e+01 1.8810192e-24 +9.8265518e+01 1.8792826e-24 +9.8492119e+01 1.8775527e-24 +9.8719243e+01 1.8758293e-24 +9.8946890e+01 1.8741124e-24 +9.9175062e+01 1.8724020e-24 +9.9403761e+01 1.8706980e-24 +9.9632987e+01 1.8690005e-24 +9.9862741e+01 1.8673094e-24 +1.0009303e+02 1.8656246e-24 +1.0032384e+02 1.8639462e-24 +1.0055519e+02 1.8622740e-24 +1.0078707e+02 1.8606081e-24 +1.0101948e+02 1.8589485e-24 +1.0125244e+02 1.8572950e-24 +1.0148593e+02 1.8556477e-24 +1.0171995e+02 1.8540066e-24 +1.0195452e+02 1.8523716e-24 +1.0218963e+02 1.8507426e-24 +1.0242528e+02 1.8491197e-24 +1.0266147e+02 1.8475029e-24 +1.0289821e+02 1.8458920e-24 +1.0313549e+02 1.8442871e-24 +1.0337332e+02 1.8426881e-24 +1.0361170e+02 1.8410951e-24 +1.0385063e+02 1.8395079e-24 +1.0409011e+02 1.8379266e-24 +1.0433015e+02 1.8363511e-24 +1.0457073e+02 1.8347815e-24 +1.0481187e+02 1.8332175e-24 +1.0505357e+02 1.8316594e-24 +1.0529582e+02 1.8301069e-24 +1.0553864e+02 1.8285602e-24 +1.0578201e+02 1.8270191e-24 +1.0602594e+02 1.8254837e-24 +1.0627044e+02 1.8239538e-24 +1.0651550e+02 1.8224296e-24 +1.0676113e+02 1.8209109e-24 +1.0700732e+02 1.8193978e-24 +1.0725408e+02 1.8178902e-24 +1.0750141e+02 1.8163881e-24 +1.0774931e+02 1.8148914e-24 +1.0799778e+02 1.8134002e-24 +1.0824682e+02 1.8119144e-24 +1.0849644e+02 1.8104340e-24 +1.0874663e+02 1.8089589e-24 +1.0899740e+02 1.8074892e-24 +1.0924875e+02 1.8060248e-24 +1.0950068e+02 1.8045657e-24 +1.0975319e+02 1.8031119e-24 +1.1000628e+02 1.8016634e-24 +1.1025996e+02 1.8002200e-24 +1.1051422e+02 1.7987819e-24 +1.1076906e+02 1.7973490e-24 +1.1102450e+02 1.7959212e-24 +1.1128052e+02 1.7944986e-24 +1.1153714e+02 1.7930811e-24 +1.1179434e+02 1.7916686e-24 +1.1205214e+02 1.7902613e-24 +1.1231053e+02 1.7888590e-24 +1.1256952e+02 1.7874618e-24 +1.1282911e+02 1.7860695e-24 +1.1308929e+02 1.7846823e-24 +1.1335008e+02 1.7833000e-24 +1.1361146e+02 1.7819227e-24 +1.1387345e+02 1.7805503e-24 +1.1413605e+02 1.7791828e-24 +1.1439924e+02 1.7778202e-24 +1.1466305e+02 1.7764625e-24 +1.1492746e+02 1.7751097e-24 +1.1519249e+02 1.7737616e-24 +1.1545812e+02 1.7724184e-24 +1.1572437e+02 1.7710800e-24 +1.1599123e+02 1.7697464e-24 +1.1625871e+02 1.7684175e-24 +1.1652680e+02 1.7670933e-24 +1.1679551e+02 1.7657739e-24 +1.1706484e+02 1.7644592e-24 +1.1733480e+02 1.7631492e-24 +1.1760537e+02 1.7618438e-24 +1.1787657e+02 1.7605431e-24 +1.1814839e+02 1.7592470e-24 +1.1842085e+02 1.7579556e-24 +1.1869392e+02 1.7566687e-24 +1.1896763e+02 1.7553865e-24 +1.1924197e+02 1.7541087e-24 +1.1951695e+02 1.7528356e-24 +1.1979255e+02 1.7515670e-24 +1.2006880e+02 1.7503028e-24 +1.2034568e+02 1.7490432e-24 +1.2062319e+02 1.7477881e-24 +1.2090135e+02 1.7465374e-24 +1.2118015e+02 1.7452912e-24 +1.2145959e+02 1.7440495e-24 +1.2173968e+02 1.7428121e-24 +1.2202041e+02 1.7415792e-24 +1.2230179e+02 1.7403506e-24 +1.2258382e+02 1.7391265e-24 +1.2286650e+02 1.7379067e-24 +1.2314983e+02 1.7366912e-24 +1.2343382e+02 1.7354801e-24 +1.2371846e+02 1.7342733e-24 +1.2400375e+02 1.7330707e-24 +1.2428971e+02 1.7318725e-24 +1.2457632e+02 1.7306786e-24 +1.2486359e+02 1.7294889e-24 +1.2515153e+02 1.7283035e-24 +1.2544013e+02 1.7271223e-24 +1.2572940e+02 1.7259453e-24 +1.2601933e+02 1.7247726e-24 +1.2630993e+02 1.7236040e-24 +1.2660120e+02 1.7224396e-24 +1.2689314e+02 1.7212794e-24 +1.2718576e+02 1.7201233e-24 +1.2747905e+02 1.7189714e-24 +1.2777302e+02 1.7178236e-24 +1.2806767e+02 1.7166800e-24 +1.2836299e+02 1.7155404e-24 +1.2865900e+02 1.7144050e-24 +1.2895569e+02 1.7132736e-24 +1.2925306e+02 1.7121463e-24 +1.2955112e+02 1.7110230e-24 +1.2984986e+02 1.7099038e-24 +1.3014930e+02 1.7087887e-24 +1.3044942e+02 1.7076776e-24 +1.3075024e+02 1.7065704e-24 +1.3105175e+02 1.7054673e-24 +1.3135396e+02 1.7043682e-24 +1.3165686e+02 1.7032731e-24 +1.3196046e+02 1.7021819e-24 +1.3226476e+02 1.7010947e-24 +1.3256977e+02 1.7000114e-24 +1.3287547e+02 1.6989321e-24 +1.3318189e+02 1.6978567e-24 +1.3348901e+02 1.6967853e-24 +1.3379683e+02 1.6957177e-24 +1.3410537e+02 1.6946541e-24 +1.3441462e+02 1.6935943e-24 +1.3472458e+02 1.6925384e-24 +1.3503525e+02 1.6914864e-24 +1.3534665e+02 1.6904383e-24 +1.3565876e+02 1.6893940e-24 +1.3597159e+02 1.6883535e-24 +1.3628514e+02 1.6873169e-24 +1.3659941e+02 1.6862841e-24 +1.3691441e+02 1.6852551e-24 +1.3723014e+02 1.6842300e-24 +1.3754659e+02 1.6832086e-24 +1.3786377e+02 1.6821910e-24 +1.3818169e+02 1.6811772e-24 +1.3850034e+02 1.6801672e-24 +1.3881972e+02 1.6791610e-24 +1.3913984e+02 1.6781585e-24 +1.3946070e+02 1.6771597e-24 +1.3978229e+02 1.6761647e-24 +1.4010463e+02 1.6751735e-24 +1.4042772e+02 1.6741859e-24 +1.4075154e+02 1.6732021e-24 +1.4107612e+02 1.6722220e-24 +1.4140144e+02 1.6712456e-24 +1.4172751e+02 1.6702729e-24 +1.4205434e+02 1.6693039e-24 +1.4238192e+02 1.6683385e-24 +1.4271025e+02 1.6673769e-24 +1.4303934e+02 1.6664189e-24 +1.4336919e+02 1.6654645e-24 +1.4369980e+02 1.6645139e-24 +1.4403117e+02 1.6635669e-24 +1.4436331e+02 1.6626235e-24 +1.4469621e+02 1.6616837e-24 +1.4502988e+02 1.6607476e-24 +1.4536432e+02 1.6598151e-24 +1.4569954e+02 1.6588863e-24 +1.4603552e+02 1.6579610e-24 +1.4637228e+02 1.6570394e-24 +1.4670981e+02 1.6561213e-24 +1.4704813e+02 1.6552069e-24 +1.4738722e+02 1.6542960e-24 +1.4772710e+02 1.6533887e-24 +1.4806776e+02 1.6524850e-24 +1.4840920e+02 1.6515849e-24 +1.4875144e+02 1.6506883e-24 +1.4909446e+02 1.6497953e-24 +1.4943827e+02 1.6489059e-24 +1.4978288e+02 1.6480200e-24 +1.5012828e+02 1.6471376e-24 +1.5047447e+02 1.6462588e-24 +1.5082147e+02 1.6453836e-24 +1.5116927e+02 1.6445119e-24 +1.5151786e+02 1.6436437e-24 +1.5186726e+02 1.6427790e-24 +1.5221747e+02 1.6419178e-24 +1.5256849e+02 1.6410602e-24 +1.5292031e+02 1.6402061e-24 +1.5327295e+02 1.6393554e-24 +1.5362639e+02 1.6385083e-24 +1.5398066e+02 1.6376647e-24 +1.5433574e+02 1.6368246e-24 +1.5469164e+02 1.6359880e-24 +1.5504836e+02 1.6351548e-24 +1.5540590e+02 1.6343252e-24 +1.5576427e+02 1.6334990e-24 +1.5612346e+02 1.6326763e-24 +1.5648348e+02 1.6318571e-24 +1.5684434e+02 1.6310413e-24 +1.5720602e+02 1.6302290e-24 +1.5756854e+02 1.6294202e-24 +1.5793189e+02 1.6286148e-24 +1.5829608e+02 1.6278129e-24 +1.5866112e+02 1.6270145e-24 +1.5902699e+02 1.6262195e-24 +1.5939371e+02 1.6254280e-24 +1.5976127e+02 1.6246399e-24 +1.6012968e+02 1.6238552e-24 +1.6049894e+02 1.6230740e-24 +1.6086905e+02 1.6222963e-24 +1.6124002e+02 1.6215219e-24 +1.6161184e+02 1.6207510e-24 +1.6198452e+02 1.6199836e-24 +1.6235805e+02 1.6192195e-24 +1.6273245e+02 1.6184589e-24 +1.6310772e+02 1.6177018e-24 +1.6348384e+02 1.6169480e-24 +1.6386084e+02 1.6161977e-24 +1.6423870e+02 1.6154508e-24 +1.6461744e+02 1.6147073e-24 +1.6499705e+02 1.6139672e-24 +1.6537753e+02 1.6132306e-24 +1.6575889e+02 1.6124973e-24 +1.6614114e+02 1.6117675e-24 +1.6652426e+02 1.6110411e-24 +1.6690826e+02 1.6103181e-24 +1.6729316e+02 1.6095985e-24 +1.6767894e+02 1.6088823e-24 +1.6806560e+02 1.6081696e-24 +1.6845316e+02 1.6074602e-24 +1.6884162e+02 1.6067542e-24 +1.6923097e+02 1.6060517e-24 +1.6962122e+02 1.6053525e-24 +1.7001236e+02 1.6046568e-24 +1.7040441e+02 1.6039644e-24 +1.7079737e+02 1.6032755e-24 +1.7119123e+02 1.6025899e-24 +1.7158600e+02 1.6019078e-24 +1.7198168e+02 1.6012290e-24 +1.7237827e+02 1.6005537e-24 +1.7277577e+02 1.5998817e-24 +1.7317419e+02 1.5992132e-24 +1.7357354e+02 1.5985480e-24 +1.7397380e+02 1.5978863e-24 +1.7437498e+02 1.5972280e-24 +1.7477709e+02 1.5965730e-24 +1.7518013e+02 1.5959215e-24 +1.7558410e+02 1.5952733e-24 +1.7598899e+02 1.5946286e-24 +1.7639483e+02 1.5939872e-24 +1.7680159e+02 1.5933493e-24 +1.7720930e+02 1.5927147e-24 +1.7761795e+02 1.5920836e-24 +1.7802753e+02 1.5914559e-24 +1.7843807e+02 1.5908315e-24 +1.7884955e+02 1.5902106e-24 +1.7926198e+02 1.5895931e-24 +1.7967535e+02 1.5889790e-24 +1.8008969e+02 1.5883682e-24 +1.8050498e+02 1.5877609e-24 +1.8092122e+02 1.5871570e-24 +1.8133843e+02 1.5865565e-24 +1.8175659e+02 1.5859595e-24 +1.8217573e+02 1.5853658e-24 +1.8259583e+02 1.5847756e-24 +1.8301689e+02 1.5841887e-24 +1.8343893e+02 1.5836053e-24 +1.8386194e+02 1.5830253e-24 +1.8428593e+02 1.5824487e-24 +1.8471089e+02 1.5818755e-24 +1.8513684e+02 1.5813058e-24 +1.8556377e+02 1.5807395e-24 +1.8599168e+02 1.5801766e-24 +1.8642058e+02 1.5796171e-24 +1.8685046e+02 1.5790611e-24 +1.8728134e+02 1.5785085e-24 +1.8771321e+02 1.5779593e-24 +1.8814608e+02 1.5774136e-24 +1.8857995e+02 1.5768713e-24 +1.8901482e+02 1.5763324e-24 +1.8945068e+02 1.5757970e-24 +1.8988756e+02 1.5752650e-24 +1.9032544e+02 1.5747365e-24 +1.9076433e+02 1.5742114e-24 +1.9120424e+02 1.5736898e-24 +1.9164516e+02 1.5731716e-24 +1.9208709e+02 1.5726569e-24 +1.9253005e+02 1.5721456e-24 +1.9297402e+02 1.5716379e-24 +1.9341902e+02 1.5711335e-24 +1.9386505e+02 1.5706327e-24 +1.9431210e+02 1.5701353e-24 +1.9476019e+02 1.5696413e-24 +1.9520930e+02 1.5691509e-24 +1.9565946e+02 1.5686639e-24 +1.9611065e+02 1.5681804e-24 +1.9656288e+02 1.5677004e-24 +1.9701616e+02 1.5672239e-24 +1.9747048e+02 1.5667509e-24 +1.9792585e+02 1.5662814e-24 +1.9838227e+02 1.5658153e-24 +1.9883974e+02 1.5653528e-24 +1.9929826e+02 1.5648938e-24 +1.9975785e+02 1.5644383e-24 +2.0021849e+02 1.5639863e-24 +2.0068020e+02 1.5635378e-24 +2.0114297e+02 1.5630928e-24 +2.0160680e+02 1.5626514e-24 +2.0207171e+02 1.5622135e-24 +2.0253769e+02 1.5617791e-24 +2.0300474e+02 1.5613482e-24 +2.0347287e+02 1.5609209e-24 +2.0394208e+02 1.5604972e-24 +2.0441237e+02 1.5600769e-24 +2.0488375e+02 1.5596603e-24 +2.0535621e+02 1.5592471e-24 +2.0582977e+02 1.5588376e-24 +2.0630441e+02 1.5584316e-24 +2.0678015e+02 1.5580292e-24 +2.0725699e+02 1.5576303e-24 +2.0773492e+02 1.5572350e-24 +2.0821396e+02 1.5568433e-24 +2.0869411e+02 1.5564552e-24 +2.0917536e+02 1.5560707e-24 +2.0965772e+02 1.5556898e-24 +2.1014119e+02 1.5553124e-24 +2.1062578e+02 1.5549387e-24 +2.1111148e+02 1.5545686e-24 +2.1159831e+02 1.5542021e-24 +2.1208625e+02 1.5538392e-24 +2.1257533e+02 1.5534799e-24 +2.1306553e+02 1.5531243e-24 +2.1355686e+02 1.5527723e-24 +2.1404932e+02 1.5524239e-24 +2.1454292e+02 1.5520792e-24 +2.1503766e+02 1.5517381e-24 +2.1553354e+02 1.5514007e-24 +2.1603056e+02 1.5510670e-24 +2.1652873e+02 1.5507369e-24 +2.1702804e+02 1.5504105e-24 +2.1752851e+02 1.5500877e-24 +2.1803013e+02 1.5497687e-24 +2.1853291e+02 1.5494533e-24 +2.1903685e+02 1.5491416e-24 +2.1954195e+02 1.5488336e-24 +2.2004822e+02 1.5485293e-24 +2.2055565e+02 1.5482287e-24 +2.2106425e+02 1.5479319e-24 +2.2157403e+02 1.5476387e-24 +2.2208498e+02 1.5473493e-24 +2.2259711e+02 1.5470637e-24 +2.2311042e+02 1.5467817e-24 +2.2362491e+02 1.5465035e-24 +2.2414060e+02 1.5462291e-24 +2.2465747e+02 1.5459584e-24 +2.2517553e+02 1.5456915e-24 +2.2569478e+02 1.5454283e-24 +2.2621524e+02 1.5451689e-24 +2.2673689e+02 1.5449133e-24 +2.2725975e+02 1.5446615e-24 +2.2778381e+02 1.5444135e-24 +2.2830908e+02 1.5441693e-24 +2.2883556e+02 1.5439289e-24 +2.2936326e+02 1.5436923e-24 +2.2989217e+02 1.5434595e-24 +2.3042231e+02 1.5432306e-24 +2.3095366e+02 1.5430055e-24 +2.3148624e+02 1.5427842e-24 +2.3202005e+02 1.5425668e-24 +2.3255509e+02 1.5423532e-24 +2.3309137e+02 1.5421435e-24 +2.3362888e+02 1.5419377e-24 +2.3416763e+02 1.5417357e-24 +2.3470762e+02 1.5415377e-24 +2.3524886e+02 1.5413435e-24 +2.3579134e+02 1.5411532e-24 +2.3633508e+02 1.5409668e-24 +2.3688007e+02 1.5407844e-24 +2.3742632e+02 1.5406058e-24 +2.3797382e+02 1.5404312e-24 +2.3852259e+02 1.5402605e-24 +2.3907263e+02 1.5400937e-24 +2.3962393e+02 1.5399309e-24 +2.4017651e+02 1.5397721e-24 +2.4073036e+02 1.5396172e-24 +2.4128548e+02 1.5394663e-24 +2.4184189e+02 1.5393194e-24 +2.4239958e+02 1.5391764e-24 +2.4295855e+02 1.5390375e-24 +2.4351882e+02 1.5389025e-24 +2.4408037e+02 1.5387716e-24 +2.4464322e+02 1.5386446e-24 +2.4520737e+02 1.5385217e-24 +2.4577282e+02 1.5384029e-24 +2.4633958e+02 1.5382881e-24 +2.4690764e+02 1.5381773e-24 +2.4747701e+02 1.5380706e-24 +2.4804769e+02 1.5379679e-24 +2.4861969e+02 1.5378694e-24 +2.4919301e+02 1.5377749e-24 +2.4976765e+02 1.5376845e-24 +2.5034362e+02 1.5375982e-24 +2.5092091e+02 1.5375160e-24 +2.5149954e+02 1.5374379e-24 +2.5207950e+02 1.5373639e-24 +2.5266080e+02 1.5372941e-24 +2.5324343e+02 1.5372285e-24 +2.5382742e+02 1.5371669e-24 +2.5441274e+02 1.5371096e-24 +2.5499942e+02 1.5370564e-24 +2.5558745e+02 1.5370073e-24 +2.5617684e+02 1.5369625e-24 +2.5676758e+02 1.5369219e-24 +2.5735969e+02 1.5368854e-24 +2.5795317e+02 1.5368532e-24 +2.5854801e+02 1.5368252e-24 +2.5914422e+02 1.5368014e-24 +2.5974181e+02 1.5367819e-24 +2.6034078e+02 1.5367666e-24 +2.6094112e+02 1.5367555e-24 +2.6154286e+02 1.5367488e-24 +2.6214598e+02 1.5367463e-24 +2.6275049e+02 1.5367481e-24 +2.6335639e+02 1.5367542e-24 +2.6396369e+02 1.5367646e-24 +2.6457240e+02 1.5367793e-24 +2.6518250e+02 1.5367983e-24 +2.6579401e+02 1.5368216e-24 +2.6640694e+02 1.5368493e-24 +2.6702127e+02 1.5368814e-24 +2.6763703e+02 1.5369178e-24 +2.6825420e+02 1.5369586e-24 +2.6887280e+02 1.5370037e-24 +2.6949282e+02 1.5370533e-24 +2.7011427e+02 1.5371073e-24 +2.7073716e+02 1.5371656e-24 +2.7136148e+02 1.5372284e-24 +2.7198724e+02 1.5372956e-24 +2.7261444e+02 1.5373673e-24 +2.7324310e+02 1.5374434e-24 +2.7387320e+02 1.5375239e-24 +2.7450475e+02 1.5376089e-24 +2.7513776e+02 1.5376984e-24 +2.7577223e+02 1.5377924e-24 +2.7640816e+02 1.5378909e-24 +2.7704556e+02 1.5379939e-24 +2.7768443e+02 1.5381015e-24 +2.7832477e+02 1.5382135e-24 +2.7896659e+02 1.5383301e-24 +2.7960989e+02 1.5384513e-24 +2.8025467e+02 1.5385770e-24 +2.8090094e+02 1.5387073e-24 +2.8154870e+02 1.5388421e-24 +2.8219796e+02 1.5389816e-24 +2.8284871e+02 1.5391257e-24 +2.8350096e+02 1.5392744e-24 +2.8415471e+02 1.5394277e-24 +2.8480998e+02 1.5395856e-24 +2.8546675e+02 1.5397482e-24 +2.8612504e+02 1.5399155e-24 +2.8678485e+02 1.5400874e-24 +2.8744617e+02 1.5402640e-24 +2.8810903e+02 1.5404453e-24 +2.8877341e+02 1.5406313e-24 +2.8943932e+02 1.5408220e-24 +2.9010677e+02 1.5410174e-24 +2.9077576e+02 1.5412175e-24 +2.9144629e+02 1.5414225e-24 +2.9211837e+02 1.5416321e-24 +2.9279200e+02 1.5418466e-24 +2.9346718e+02 1.5420658e-24 +2.9414391e+02 1.5422898e-24 +2.9482221e+02 1.5425186e-24 +2.9550207e+02 1.5427522e-24 +2.9618350e+02 1.5429907e-24 +2.9686651e+02 1.5432339e-24 +2.9755108e+02 1.5434821e-24 +2.9823724e+02 1.5437351e-24 +2.9892498e+02 1.5439929e-24 +2.9961430e+02 1.5442557e-24 +3.0030521e+02 1.5445233e-24 +3.0099772e+02 1.5447959e-24 +3.0169182e+02 1.5450733e-24 +3.0238752e+02 1.5453557e-24 +3.0308483e+02 1.5456431e-24 +3.0378375e+02 1.5459354e-24 +3.0448428e+02 1.5462326e-24 +3.0518642e+02 1.5465349e-24 +3.0589018e+02 1.5468421e-24 +3.0659557e+02 1.5471543e-24 +3.0730258e+02 1.5474716e-24 +3.0801122e+02 1.5477938e-24 +3.0872150e+02 1.5481211e-24 +3.0943341e+02 1.5484535e-24 +3.1014697e+02 1.5487909e-24 +3.1086217e+02 1.5491334e-24 +3.1157902e+02 1.5494809e-24 +3.1229752e+02 1.5498336e-24 +3.1301768e+02 1.5501914e-24 +3.1373950e+02 1.5505543e-24 +3.1446299e+02 1.5509224e-24 +3.1518814e+02 1.5512955e-24 +3.1591497e+02 1.5516739e-24 +3.1664347e+02 1.5520574e-24 +3.1737365e+02 1.5524461e-24 +3.1810552e+02 1.5528400e-24 +3.1883907e+02 1.5532392e-24 +3.1957432e+02 1.5536435e-24 +3.2031126e+02 1.5540531e-24 +3.2104990e+02 1.5544679e-24 +3.2179024e+02 1.5548880e-24 +3.2253229e+02 1.5553133e-24 +3.2327606e+02 1.5557440e-24 +3.2402153e+02 1.5561799e-24 +3.2476873e+02 1.5566212e-24 +3.2551765e+02 1.5570678e-24 +3.2626829e+02 1.5575197e-24 +3.2702067e+02 1.5579770e-24 +3.2777478e+02 1.5584397e-24 +3.2853063e+02 1.5589077e-24 +3.2928823e+02 1.5593811e-24 +3.3004757e+02 1.5598599e-24 +3.3080866e+02 1.5603441e-24 +3.3157151e+02 1.5608338e-24 +3.3233612e+02 1.5613289e-24 +3.3310249e+02 1.5618295e-24 +3.3387062e+02 1.5623355e-24 +3.3464053e+02 1.5628470e-24 +3.3541221e+02 1.5633641e-24 +3.3618568e+02 1.5638866e-24 +3.3696092e+02 1.5644146e-24 +3.3773796e+02 1.5649482e-24 +3.3851679e+02 1.5654874e-24 +3.3929741e+02 1.5660321e-24 +3.4007983e+02 1.5665824e-24 +3.4086406e+02 1.5671382e-24 +3.4165009e+02 1.5676997e-24 +3.4243794e+02 1.5682668e-24 +3.4322760e+02 1.5688395e-24 +3.4401909e+02 1.5694179e-24 +3.4481240e+02 1.5700020e-24 +3.4560754e+02 1.5705917e-24 +3.4640451e+02 1.5711871e-24 +3.4720333e+02 1.5717882e-24 +3.4800398e+02 1.5723950e-24 +3.4880648e+02 1.5730075e-24 +3.4961083e+02 1.5736258e-24 +3.5041703e+02 1.5742499e-24 +3.5122510e+02 1.5748797e-24 +3.5203503e+02 1.5755153e-24 +3.5284682e+02 1.5761567e-24 +3.5366049e+02 1.5768039e-24 +3.5447603e+02 1.5774569e-24 +3.5529346e+02 1.5781158e-24 +3.5611277e+02 1.5787806e-24 +3.5693397e+02 1.5794512e-24 +3.5775706e+02 1.5801277e-24 +3.5858205e+02 1.5808101e-24 +3.5940894e+02 1.5814984e-24 +3.6023774e+02 1.5821926e-24 +3.6106845e+02 1.5828928e-24 +3.6190108e+02 1.5835989e-24 +3.6273563e+02 1.5843110e-24 +3.6357210e+02 1.5850291e-24 +3.6441050e+02 1.5857532e-24 +3.6525083e+02 1.5864833e-24 +3.6609310e+02 1.5872195e-24 +3.6693731e+02 1.5879616e-24 +3.6778347e+02 1.5887099e-24 +3.6863159e+02 1.5894642e-24 +3.6948165e+02 1.5902246e-24 +3.7033368e+02 1.5909911e-24 +3.7118767e+02 1.5917637e-24 +3.7204364e+02 1.5925424e-24 +3.7290157e+02 1.5933273e-24 +3.7376148e+02 1.5941184e-24 +3.7462338e+02 1.5949156e-24 +3.7548727e+02 1.5957190e-24 +3.7635314e+02 1.5965287e-24 +3.7722102e+02 1.5973445e-24 +3.7809089e+02 1.5981666e-24 +3.7896277e+02 1.5989949e-24 +3.7983666e+02 1.5998296e-24 +3.8071257e+02 1.6006704e-24 +3.8159049e+02 1.6015176e-24 +3.8247044e+02 1.6023711e-24 +3.8335242e+02 1.6032309e-24 +3.8423644e+02 1.6040971e-24 +3.8512249e+02 1.6049696e-24 +3.8601059e+02 1.6058485e-24 +3.8690073e+02 1.6067338e-24 +3.8779292e+02 1.6076255e-24 +3.8868718e+02 1.6085236e-24 +3.8958349e+02 1.6094282e-24 +3.9048188e+02 1.6103392e-24 +3.9138233e+02 1.6112566e-24 +3.9228486e+02 1.6121806e-24 +3.9318947e+02 1.6131110e-24 +3.9409617e+02 1.6140479e-24 +3.9500496e+02 1.6149914e-24 +3.9591584e+02 1.6159414e-24 +3.9682883e+02 1.6168980e-24 +3.9774392e+02 1.6178612e-24 +3.9866112e+02 1.6188309e-24 +3.9958044e+02 1.6198073e-24 +4.0050187e+02 1.6207903e-24 +4.0142543e+02 1.6217799e-24 +4.0235112e+02 1.6227762e-24 +4.0327895e+02 1.6237791e-24 +4.0420891e+02 1.6247888e-24 +4.0514102e+02 1.6258051e-24 +4.0607528e+02 1.6268282e-24 +4.0701169e+02 1.6278580e-24 +4.0795026e+02 1.6288945e-24 +4.0889100e+02 1.6299378e-24 +4.0983390e+02 1.6309879e-24 +4.1077898e+02 1.6320449e-24 +4.1172624e+02 1.6331086e-24 +4.1267569e+02 1.6341792e-24 +4.1362732e+02 1.6352566e-24 +4.1458115e+02 1.6363409e-24 +4.1553718e+02 1.6374320e-24 +4.1649541e+02 1.6385301e-24 +4.1745585e+02 1.6396351e-24 +4.1841851e+02 1.6407471e-24 +4.1938338e+02 1.6418660e-24 +4.2035048e+02 1.6429918e-24 +4.2131982e+02 1.6441247e-24 +4.2229138e+02 1.6452646e-24 +4.2326519e+02 1.6464115e-24 +4.2424124e+02 1.6475654e-24 +4.2521955e+02 1.6487264e-24 +4.2620010e+02 1.6498945e-24 +4.2718293e+02 1.6510697e-24 +4.2816801e+02 1.6522520e-24 +4.2915537e+02 1.6534415e-24 +4.3014501e+02 1.6546381e-24 +4.3113692e+02 1.6558419e-24 +4.3213113e+02 1.6570529e-24 +4.3312763e+02 1.6582711e-24 +4.3412642e+02 1.6594966e-24 +4.3512752e+02 1.6607293e-24 +4.3613093e+02 1.6619693e-24 +4.3713665e+02 1.6632166e-24 +4.3814469e+02 1.6644713e-24 +4.3915506e+02 1.6657333e-24 +4.4016775e+02 1.6670026e-24 +4.4118278e+02 1.6682794e-24 +4.4220015e+02 1.6695636e-24 +4.4321987e+02 1.6708553e-24 +4.4424194e+02 1.6721544e-24 +4.4526636e+02 1.6734610e-24 +4.4629315e+02 1.6747752e-24 +4.4732231e+02 1.6760970e-24 +4.4835383e+02 1.6774264e-24 +4.4938774e+02 1.6787634e-24 +4.5042403e+02 1.6801081e-24 +4.5146271e+02 1.6814605e-24 +4.5250379e+02 1.6828207e-24 +4.5354727e+02 1.6841886e-24 +4.5459315e+02 1.6855645e-24 +4.5564145e+02 1.6869483e-24 +4.5669216e+02 1.6883400e-24 +4.5774529e+02 1.6897398e-24 +4.5880086e+02 1.6911477e-24 +4.5985886e+02 1.6925639e-24 +4.6091930e+02 1.6939883e-24 +4.6198218e+02 1.6954211e-24 +4.6304751e+02 1.6968625e-24 +4.6411530e+02 1.6983125e-24 +4.6518556e+02 1.6997713e-24 +4.6625828e+02 1.7012392e-24 +4.6733347e+02 1.7027162e-24 +4.6841115e+02 1.7042028e-24 +4.6949131e+02 1.7056992e-24 +4.7057396e+02 1.7072057e-24 +4.7165911e+02 1.7087228e-24 +4.7274676e+02 1.7102512e-24 +4.7383691e+02 1.7117914e-24 +4.7492958e+02 1.7133444e-24 +4.7602478e+02 1.7149112e-24 +4.7712249e+02 1.7164933e-24 +4.7822274e+02 1.7180925e-24 +4.7932553e+02 1.7197115e-24 +4.8043085e+02 1.7213535e-24 +4.8153873e+02 1.7230232e-24 +4.8264916e+02 1.7247274e-24 +4.8376216e+02 1.7264758e-24 +4.8487772e+02 1.7282831e-24 +4.8599585e+02 1.7301722e-24 +4.8711656e+02 1.7321808e-24 +4.8823985e+02 1.7343741e-24 +4.8936574e+02 1.7368731e-24 +4.9049422e+02 1.7399239e-24 +4.9162530e+02 1.7440929e-24 +4.9275899e+02 1.7509377e-24 +4.9389530e+02 1.7661487e-24 +4.9503423e+02 1.8256271e-24 +4.9617578e+02 3.4056757e-24 +4.9731996e+02 1.9474567e-24 +4.9846679e+02 1.7863503e-24 +4.9961626e+02 1.7644707e-24 +5.0076837e+02 1.7586568e-24 +5.0192315e+02 1.7570630e-24 +5.0308059e+02 1.7570463e-24 +5.0424070e+02 1.7577521e-24 +5.0540348e+02 1.7588369e-24 +5.0656894e+02 1.7601411e-24 +5.0773710e+02 1.7615825e-24 +5.0890794e+02 1.7631154e-24 +5.1008149e+02 1.7647125e-24 +5.1125774e+02 1.7663569e-24 +5.1243670e+02 1.7680378e-24 +5.1361839e+02 1.7697476e-24 +5.1480279e+02 1.7714813e-24 +5.1598993e+02 1.7732353e-24 +5.1717981e+02 1.7750070e-24 +5.1837243e+02 1.7767944e-24 +5.1956780e+02 1.7785962e-24 +5.2076593e+02 1.7804112e-24 +5.2196682e+02 1.7822386e-24 +5.2317048e+02 1.7840777e-24 +5.2437691e+02 1.7859280e-24 +5.2558613e+02 1.7877891e-24 +5.2679814e+02 1.7896607e-24 +5.2801294e+02 1.7915425e-24 +5.2923054e+02 1.7934343e-24 +5.3045095e+02 1.7953359e-24 +5.3167417e+02 1.7972472e-24 +5.3290022e+02 1.7991680e-24 +5.3412909e+02 1.8010983e-24 +5.3536079e+02 1.8030380e-24 +5.3659534e+02 1.8049870e-24 +5.3783273e+02 1.8069454e-24 +5.3907298e+02 1.8089129e-24 +5.4031609e+02 1.8108896e-24 +5.4156206e+02 1.8128755e-24 +5.4281090e+02 1.8148706e-24 +5.4406263e+02 1.8168748e-24 +5.4531724e+02 1.8188881e-24 +5.4657475e+02 1.8209106e-24 +5.4783515e+02 1.8229421e-24 +5.4909847e+02 1.8249828e-24 +5.5036469e+02 1.8270326e-24 +5.5163384e+02 1.8290915e-24 +5.5290591e+02 1.8311594e-24 +5.5418091e+02 1.8332366e-24 +5.5545886e+02 1.8353228e-24 +5.5673975e+02 1.8374182e-24 +5.5802360e+02 1.8395227e-24 +5.5931040e+02 1.8416364e-24 +5.6060018e+02 1.8437592e-24 +5.6189293e+02 1.8458912e-24 +5.6318866e+02 1.8480324e-24 +5.6448737e+02 1.8501828e-24 +5.6578909e+02 1.8523424e-24 +5.6709380e+02 1.8545112e-24 +5.6840152e+02 1.8566893e-24 +5.6971226e+02 1.8588766e-24 +5.7102602e+02 1.8610732e-24 +5.7234281e+02 1.8632790e-24 +5.7366264e+02 1.8654942e-24 +5.7498551e+02 1.8677187e-24 +5.7631143e+02 1.8699525e-24 +5.7764041e+02 1.8721956e-24 +5.7897245e+02 1.8744482e-24 +5.8030757e+02 1.8767101e-24 +5.8164576e+02 1.8789813e-24 +5.8298704e+02 1.8812620e-24 +5.8433141e+02 1.8835522e-24 +5.8567889e+02 1.8858517e-24 +5.8702947e+02 1.8881608e-24 +5.8838316e+02 1.8904793e-24 +5.8973998e+02 1.8928073e-24 +5.9109992e+02 1.8951448e-24 +5.9246300e+02 1.8974919e-24 +5.9382923e+02 1.8998485e-24 +5.9519860e+02 1.9022147e-24 +5.9657113e+02 1.9045904e-24 +5.9794683e+02 1.9069758e-24 +5.9932570e+02 1.9093708e-24 +6.0070775e+02 1.9117754e-24 +6.0209299e+02 1.9141897e-24 +6.0348142e+02 1.9166136e-24 +6.0487305e+02 1.9190473e-24 +6.0626789e+02 1.9214906e-24 +6.0766595e+02 1.9239437e-24 +6.0906723e+02 1.9264066e-24 +6.1047175e+02 1.9288792e-24 +6.1187950e+02 1.9313616e-24 +6.1329050e+02 1.9338538e-24 +6.1470475e+02 1.9363558e-24 +6.1612226e+02 1.9388676e-24 +6.1754305e+02 1.9413893e-24 +6.1896711e+02 1.9439209e-24 +6.2039445e+02 1.9464624e-24 +6.2182508e+02 1.9490138e-24 +6.2325902e+02 1.9515752e-24 +6.2469626e+02 1.9541464e-24 +6.2613681e+02 1.9567277e-24 +6.2758069e+02 1.9593190e-24 +6.2902789e+02 1.9619202e-24 +6.3047844e+02 1.9645315e-24 +6.3193232e+02 1.9671528e-24 +6.3338957e+02 1.9697842e-24 +6.3485017e+02 1.9724257e-24 +6.3631414e+02 1.9750773e-24 +6.3778148e+02 1.9777390e-24 +6.3925221e+02 1.9804109e-24 +6.4072633e+02 1.9830929e-24 +6.4220385e+02 1.9857851e-24 +6.4368478e+02 1.9884875e-24 +6.4516912e+02 1.9912001e-24 +6.4665688e+02 1.9939230e-24 +6.4814808e+02 1.9966561e-24 +6.4964271e+02 1.9993995e-24 +6.5114080e+02 2.0021532e-24 +6.5264233e+02 2.0049172e-24 +6.5414733e+02 2.0076916e-24 +6.5565580e+02 2.0104763e-24 +6.5716775e+02 2.0132713e-24 +6.5868318e+02 2.0160768e-24 +6.6020211e+02 2.0188927e-24 +6.6172454e+02 2.0217190e-24 +6.6325048e+02 2.0245558e-24 +6.6477994e+02 2.0274030e-24 +6.6631293e+02 2.0302607e-24 +6.6784945e+02 2.0331290e-24 +6.6938952e+02 2.0360078e-24 +6.7093314e+02 2.0388971e-24 +6.7248031e+02 2.0417970e-24 +6.7403106e+02 2.0447075e-24 +6.7558538e+02 2.0476286e-24 +6.7714328e+02 2.0505603e-24 +6.7870478e+02 2.0535027e-24 +6.8026988e+02 2.0564557e-24 +6.8183859e+02 2.0594195e-24 +6.8341091e+02 2.0623939e-24 +6.8498686e+02 2.0653791e-24 +6.8656645e+02 2.0683751e-24 +6.8814967e+02 2.0713818e-24 +6.8973655e+02 2.0743993e-24 +6.9132709e+02 2.0774276e-24 +6.9292130e+02 2.0804667e-24 +6.9451918e+02 2.0835167e-24 +6.9612074e+02 2.0865776e-24 +6.9772600e+02 2.0896493e-24 +6.9933497e+02 2.0927320e-24 +7.0094764e+02 2.0958256e-24 +7.0256403e+02 2.0989301e-24 +7.0418415e+02 2.1020457e-24 +7.0580800e+02 2.1051722e-24 +7.0743560e+02 2.1083097e-24 +7.0906695e+02 2.1114583e-24 +7.1070206e+02 2.1146179e-24 +7.1234095e+02 2.1177886e-24 +7.1398361e+02 2.1209704e-24 +7.1563006e+02 2.1241633e-24 +7.1728031e+02 2.1273674e-24 +7.1893437e+02 2.1305826e-24 +7.2059223e+02 2.1338090e-24 +7.2225393e+02 2.1370466e-24 +7.2391945e+02 2.1402954e-24 +7.2558881e+02 2.1435554e-24 +7.2726203e+02 2.1468267e-24 +7.2893910e+02 2.1501093e-24 +7.3062004e+02 2.1534032e-24 +7.3230485e+02 2.1567085e-24 +7.3399355e+02 2.1600250e-24 +7.3568615e+02 2.1633530e-24 +7.3738265e+02 2.1666923e-24 +7.3908306e+02 2.1700430e-24 +7.4078739e+02 2.1734052e-24 +7.4249565e+02 2.1767788e-24 +7.4420785e+02 2.1801639e-24 +7.4592400e+02 2.1835605e-24 +7.4764410e+02 2.1869686e-24 +7.4936818e+02 2.1903883e-24 +7.5109623e+02 2.1938195e-24 +7.5282826e+02 2.1972622e-24 +7.5456429e+02 2.2007166e-24 +7.5630432e+02 2.2041826e-24 +7.5804836e+02 2.2076603e-24 +7.5979643e+02 2.2111496e-24 +7.6154852e+02 2.2146506e-24 +7.6330466e+02 2.2181633e-24 +7.6506485e+02 2.2216877e-24 +7.6682909e+02 2.2252239e-24 +7.6859741e+02 2.2287719e-24 +7.7036980e+02 2.2323316e-24 +7.7214628e+02 2.2359032e-24 +7.7392685e+02 2.2394866e-24 +7.7571153e+02 2.2430819e-24 +7.7750033e+02 2.2466890e-24 +7.7929325e+02 2.2503081e-24 +7.8109031e+02 2.2539391e-24 +7.8289151e+02 2.2575820e-24 +7.8469686e+02 2.2612369e-24 +7.8650638e+02 2.2649038e-24 +7.8832007e+02 2.2685828e-24 +7.9013794e+02 2.2722737e-24 +7.9196000e+02 2.2759767e-24 +7.9378627e+02 2.2796918e-24 +7.9561675e+02 2.2834190e-24 +7.9745144e+02 2.2871584e-24 +7.9929037e+02 2.2909098e-24 +8.0113354e+02 2.2946735e-24 +8.0298096e+02 2.2984493e-24 +8.0483264e+02 2.3022374e-24 +8.0668859e+02 2.3060377e-24 +8.0854882e+02 2.3098503e-24 +8.1041334e+02 2.3136751e-24 +8.1228216e+02 2.3175123e-24 +8.1415529e+02 2.3213618e-24 +8.1603274e+02 2.3252236e-24 +8.1791452e+02 2.3290978e-24 +8.1980064e+02 2.3329844e-24 +8.2169110e+02 2.3368835e-24 +8.2358593e+02 2.3407949e-24 +8.2548512e+02 2.3447189e-24 +8.2738870e+02 2.3486553e-24 +8.2929666e+02 2.3526043e-24 +8.3120903e+02 2.3565658e-24 +8.3312580e+02 2.3605398e-24 +8.3504700e+02 2.3645265e-24 +8.3697262e+02 2.3685257e-24 +8.3890269e+02 2.3725376e-24 +8.4083720e+02 2.3765621e-24 +8.4277618e+02 2.3805993e-24 +8.4471963e+02 2.3846492e-24 +8.4666756e+02 2.3887118e-24 +8.4861998e+02 2.3927872e-24 +8.5057690e+02 2.3968754e-24 +8.5253834e+02 2.4009763e-24 +8.5450430e+02 2.4050901e-24 +8.5647479e+02 2.4092167e-24 +8.5844983e+02 2.4133561e-24 +8.6042942e+02 2.4175085e-24 +8.6241358e+02 2.4216738e-24 +8.6440231e+02 2.4258520e-24 +8.6639563e+02 2.4300432e-24 +8.6839354e+02 2.4342474e-24 +8.7039607e+02 2.4384645e-24 +8.7240321e+02 2.4426948e-24 +8.7441498e+02 2.4469380e-24 +8.7643138e+02 2.4511944e-24 +8.7845244e+02 2.4554639e-24 +8.8047816e+02 2.4597465e-24 +8.8250855e+02 2.4640422e-24 +8.8454362e+02 2.4683512e-24 +8.8658338e+02 2.4726733e-24 +8.8862785e+02 2.4770087e-24 +8.9067703e+02 2.4813573e-24 +8.9273094e+02 2.4857193e-24 +8.9478959e+02 2.4900945e-24 +8.9685298e+02 2.4944831e-24 +8.9892113e+02 2.4988850e-24 +9.0099405e+02 2.5033003e-24 +9.0307175e+02 2.5077290e-24 +9.0515424e+02 2.5121711e-24 +9.0724153e+02 2.5166267e-24 +9.0933364e+02 2.5210958e-24 +9.1143057e+02 2.5255784e-24 +9.1353233e+02 2.5300746e-24 +9.1563894e+02 2.5345843e-24 +9.1775041e+02 2.5391076e-24 +9.1986675e+02 2.5436445e-24 +9.2198797e+02 2.5481950e-24 +9.2411408e+02 2.5527593e-24 +9.2624510e+02 2.5573372e-24 +9.2838103e+02 2.5619288e-24 +9.3052188e+02 2.5665342e-24 +9.3266767e+02 2.5711534e-24 +9.3481841e+02 2.5757864e-24 +9.3697411e+02 2.5804333e-24 +9.3913478e+02 2.5850940e-24 +9.4130043e+02 2.5897687e-24 +9.4347108e+02 2.5944573e-24 +9.4564673e+02 2.5991598e-24 +9.4782740e+02 2.6038764e-24 +9.5001309e+02 2.6086071e-24 +9.5220383e+02 2.6133519e-24 +9.5439962e+02 2.6181108e-24 +9.5660047e+02 2.6228840e-24 +9.5880640e+02 2.6276715e-24 +9.6101742e+02 2.6324734e-24 +9.6323353e+02 2.6372899e-24 +9.6545475e+02 2.6421210e-24 +9.6768110e+02 2.6469671e-24 +9.6991258e+02 2.6518284e-24 +9.7214920e+02 2.6567055e-24 +9.7439099e+02 2.6615992e-24 +9.7663794e+02 2.6665109e-24 +9.7889008e+02 2.6714432e-24 +9.8114740e+02 2.6764013e-24 +9.8340994e+02 2.6813977e-24 +9.8567769e+02 2.6864668e-24 +9.8795067e+02 2.6917392e-24 +9.9022889e+02 2.6981168e-24 +9.9251237e+02 2.8068602e-24 +9.9480111e+02 2.7097537e-24 +9.9709513e+02 2.7119434e-24 +9.9939443e+02 2.7165529e-24 +1.0016990e+03 2.7214696e-24 +1.0040090e+03 2.7264795e-24 +1.0063242e+03 2.7315338e-24 +1.0086448e+03 2.7366165e-24 +1.0109708e+03 2.7417209e-24 +1.0133021e+03 2.7468442e-24 +1.0156387e+03 2.7519848e-24 +1.0179808e+03 2.7571417e-24 +1.0203283e+03 2.7623146e-24 +1.0226812e+03 2.7675030e-24 +1.0250395e+03 2.7727069e-24 +1.0274032e+03 2.7779260e-24 +1.0297724e+03 2.7831604e-24 +1.0321471e+03 2.7884100e-24 +1.0345272e+03 2.7936747e-24 +1.0369129e+03 2.7989546e-24 +1.0393040e+03 2.8042496e-24 +1.0417006e+03 2.8095598e-24 +1.0441028e+03 2.8148852e-24 +1.0465105e+03 2.8202257e-24 +1.0489238e+03 2.8255815e-24 +1.0513426e+03 2.8309524e-24 +1.0537670e+03 2.8363386e-24 +1.0561970e+03 2.8417401e-24 +1.0586326e+03 2.8471569e-24 +1.0610738e+03 2.8525890e-24 +1.0635207e+03 2.8580365e-24 +1.0659731e+03 2.8634993e-24 +1.0684313e+03 2.8689775e-24 +1.0708951e+03 2.8744712e-24 +1.0733646e+03 2.8799803e-24 +1.0758398e+03 2.8855049e-24 +1.0783207e+03 2.8910450e-24 +1.0808073e+03 2.8966006e-24 +1.0832996e+03 2.9021719e-24 +1.0857977e+03 2.9077587e-24 +1.0883016e+03 2.9133612e-24 +1.0908112e+03 2.9189793e-24 +1.0933266e+03 2.9246131e-24 +1.0958479e+03 2.9302626e-24 +1.0983749e+03 2.9359279e-24 +1.1009078e+03 2.9416089e-24 +1.1034465e+03 2.9473058e-24 +1.1059910e+03 2.9530185e-24 +1.1085414e+03 2.9587471e-24 +1.1110977e+03 2.9644916e-24 +1.1136599e+03 2.9702520e-24 +1.1162281e+03 2.9760283e-24 +1.1188021e+03 2.9818207e-24 +1.1213820e+03 2.9876291e-24 +1.1239680e+03 2.9934535e-24 +1.1265598e+03 2.9992941e-24 +1.1291577e+03 3.0051507e-24 +1.1317615e+03 3.0110235e-24 +1.1343714e+03 3.0169125e-24 +1.1369873e+03 3.0228177e-24 +1.1396092e+03 3.0287391e-24 +1.1422371e+03 3.0346768e-24 +1.1448711e+03 3.0406309e-24 +1.1475112e+03 3.0466012e-24 +1.1501574e+03 3.0525879e-24 +1.1528096e+03 3.0585911e-24 +1.1554680e+03 3.0646106e-24 +1.1581325e+03 3.0706467e-24 +1.1608032e+03 3.0766992e-24 +1.1634800e+03 3.0827682e-24 +1.1661630e+03 3.0888539e-24 +1.1688522e+03 3.0949561e-24 +1.1715476e+03 3.1010749e-24 +1.1742492e+03 3.1072104e-24 +1.1769570e+03 3.1133626e-24 +1.1796711e+03 3.1195315e-24 +1.1823914e+03 3.1257172e-24 +1.1851180e+03 3.1319197e-24 +1.1878509e+03 3.1381390e-24 +1.1905901e+03 3.1443752e-24 +1.1933356e+03 3.1506282e-24 +1.1960875e+03 3.1568982e-24 +1.1988456e+03 3.1631851e-24 +1.2016102e+03 3.1694891e-24 +1.2043811e+03 3.1758100e-24 +1.2071584e+03 3.1821480e-24 +1.2099421e+03 3.1885031e-24 +1.2127323e+03 3.1948754e-24 +1.2155288e+03 3.2012647e-24 +1.2183319e+03 3.2076713e-24 +1.2211413e+03 3.2140951e-24 +1.2239573e+03 3.2205362e-24 +1.2267798e+03 3.2269946e-24 +1.2296087e+03 3.2334703e-24 +1.2324442e+03 3.2399633e-24 +1.2352862e+03 3.2464738e-24 +1.2381348e+03 3.2530017e-24 +1.2409900e+03 3.2595471e-24 +1.2438517e+03 3.2661100e-24 +1.2467200e+03 3.2726904e-24 +1.2495950e+03 3.2792884e-24 +1.2524766e+03 3.2859039e-24 +1.2553648e+03 3.2925372e-24 +1.2582597e+03 3.2991881e-24 +1.2611612e+03 3.3058567e-24 +1.2640695e+03 3.3125431e-24 +1.2669844e+03 3.3192473e-24 +1.2699061e+03 3.3259693e-24 +1.2728345e+03 3.3327091e-24 +1.2757697e+03 3.3394668e-24 +1.2787116e+03 3.3462425e-24 +1.2816603e+03 3.3530361e-24 +1.2846158e+03 3.3598477e-24 +1.2875782e+03 3.3666774e-24 +1.2905473e+03 3.3735251e-24 +1.2935233e+03 3.3803909e-24 +1.2965062e+03 3.3872749e-24 +1.2994960e+03 3.3941771e-24 +1.3024926e+03 3.4010975e-24 +1.3054962e+03 3.4080361e-24 +1.3085067e+03 3.4149930e-24 +1.3115241e+03 3.4219683e-24 +1.3145485e+03 3.4289619e-24 +1.3175798e+03 3.4359739e-24 +1.3206182e+03 3.4430044e-24 +1.3236635e+03 3.4500533e-24 +1.3267159e+03 3.4571207e-24 +1.3297753e+03 3.4642067e-24 +1.3328418e+03 3.4713113e-24 +1.3359154e+03 3.4784345e-24 +1.3389960e+03 3.4855764e-24 +1.3420837e+03 3.4927369e-24 +1.3451786e+03 3.4999162e-24 +1.3482806e+03 3.5071143e-24 +1.3513897e+03 3.5143312e-24 +1.3545060e+03 3.5215670e-24 +1.3576295e+03 3.5288216e-24 +1.3607602e+03 3.5360952e-24 +1.3638982e+03 3.5433877e-24 +1.3670433e+03 3.5506993e-24 +1.3701957e+03 3.5580299e-24 +1.3733554e+03 3.5653795e-24 +1.3765224e+03 3.5727483e-24 +1.3796966e+03 3.5801363e-24 +1.3828782e+03 3.5875435e-24 +1.3860672e+03 3.5949699e-24 +1.3892634e+03 3.6024155e-24 +1.3924671e+03 3.6098806e-24 +1.3956781e+03 3.6173649e-24 +1.3988966e+03 3.6248687e-24 +1.4021224e+03 3.6323919e-24 +1.4053558e+03 3.6399345e-24 +1.4085965e+03 3.6474967e-24 +1.4118448e+03 3.6550785e-24 +1.4151005e+03 3.6626799e-24 +1.4183637e+03 3.6703008e-24 +1.4216345e+03 3.6779415e-24 +1.4249128e+03 3.6856019e-24 +1.4281986e+03 3.6932821e-24 +1.4314921e+03 3.7009821e-24 +1.4347931e+03 3.7087019e-24 +1.4381017e+03 3.7164417e-24 +1.4414180e+03 3.7242014e-24 +1.4447419e+03 3.7319811e-24 +1.4480735e+03 3.7397808e-24 +1.4514128e+03 3.7476006e-24 +1.4547598e+03 3.7554407e-24 +1.4581144e+03 3.7633010e-24 +1.4614769e+03 3.7711816e-24 +1.4648470e+03 3.7790828e-24 +1.4682250e+03 3.7870049e-24 +1.4716107e+03 3.7949483e-24 +1.4750043e+03 3.8029141e-24 +1.4784056e+03 3.8109056e-24 +1.4818149e+03 3.8189344e-24 +1.4852319e+03 3.8270783e-24 +1.4886569e+03 3.8420816e-24 +1.4920897e+03 3.8433663e-24 +1.4955305e+03 3.8511660e-24 +1.4989792e+03 3.8592287e-24 +1.5024359e+03 3.8673404e-24 +1.5059005e+03 3.8754803e-24 +1.5093731e+03 3.8836438e-24 +1.5128538e+03 3.8918294e-24 +1.5163424e+03 3.9000365e-24 +1.5198391e+03 3.9082648e-24 +1.5233439e+03 3.9165144e-24 +1.5268567e+03 3.9247850e-24 +1.5303776e+03 3.9330768e-24 +1.5339067e+03 3.9413897e-24 +1.5374439e+03 3.9497238e-24 +1.5409893e+03 3.9580790e-24 +1.5445428e+03 3.9664555e-24 +1.5481045e+03 3.9748532e-24 +1.5516745e+03 3.9832722e-24 +1.5552526e+03 3.9917126e-24 +1.5588391e+03 4.0001743e-24 +1.5624338e+03 4.0086575e-24 +1.5660367e+03 4.0171621e-24 +1.5696480e+03 4.0256882e-24 +1.5732677e+03 4.0342359e-24 +1.5768956e+03 4.0428052e-24 +1.5805320e+03 4.0513961e-24 +1.5841767e+03 4.0600088e-24 +1.5878298e+03 4.0686431e-24 +1.5914913e+03 4.0772992e-24 +1.5951613e+03 4.0859772e-24 +1.5988398e+03 4.0946770e-24 +1.6025267e+03 4.1033987e-24 +1.6062222e+03 4.1121424e-24 +1.6099261e+03 4.1209081e-24 +1.6136386e+03 4.1296958e-24 +1.6173597e+03 4.1385057e-24 +1.6210893e+03 4.1473376e-24 +1.6248276e+03 4.1561918e-24 +1.6285744e+03 4.1650682e-24 +1.6323300e+03 4.1739669e-24 +1.6360941e+03 4.1828879e-24 +1.6398670e+03 4.1918313e-24 +1.6436485e+03 4.2007971e-24 +1.6474388e+03 4.2097854e-24 +1.6512378e+03 4.2187962e-24 +1.6550456e+03 4.2278296e-24 +1.6588621e+03 4.2368856e-24 +1.6626874e+03 4.2459642e-24 +1.6665216e+03 4.2550656e-24 +1.6703646e+03 4.2641897e-24 +1.6742165e+03 4.2733366e-24 +1.6780773e+03 4.2825064e-24 +1.6819469e+03 4.2916990e-24 +1.6858255e+03 4.3009147e-24 +1.6897130e+03 4.3101533e-24 +1.6936095e+03 4.3194149e-24 +1.6975150e+03 4.3286997e-24 +1.7014295e+03 4.3380076e-24 +1.7053530e+03 4.3473387e-24 +1.7092855e+03 4.3566930e-24 +1.7132272e+03 4.3660706e-24 +1.7171779e+03 4.3754716e-24 +1.7211377e+03 4.3848960e-24 +1.7251067e+03 4.3943437e-24 +1.7290848e+03 4.4038150e-24 +1.7330721e+03 4.4133099e-24 +1.7370685e+03 4.4228283e-24 +1.7410742e+03 4.4323703e-24 +1.7450892e+03 4.4419361e-24 +1.7491133e+03 4.4515256e-24 +1.7531468e+03 4.4611388e-24 +1.7571896e+03 4.4707759e-24 +1.7612417e+03 4.4804369e-24 +1.7653031e+03 4.4901219e-24 +1.7693739e+03 4.4998308e-24 +1.7734541e+03 4.5095638e-24 +1.7775437e+03 4.5193209e-24 +1.7816427e+03 4.5291021e-24 +1.7857512e+03 4.5389076e-24 +1.7898692e+03 4.5487373e-24 +1.7939966e+03 4.5585912e-24 +1.7981336e+03 4.5684696e-24 +1.8022801e+03 4.5783723e-24 +1.8064362e+03 4.5882995e-24 +1.8106018e+03 4.5982513e-24 +1.8147771e+03 4.6082275e-24 +1.8189620e+03 4.6182284e-24 +1.8231565e+03 4.6282540e-24 +1.8273607e+03 4.6383043e-24 +1.8315746e+03 4.6483793e-24 +1.8357983e+03 4.6584792e-24 +1.8400316e+03 4.6686040e-24 +1.8442748e+03 4.6787537e-24 +1.8485277e+03 4.6889284e-24 +1.8527904e+03 4.6991281e-24 +1.8570629e+03 4.7093529e-24 +1.8613453e+03 4.7196029e-24 +1.8656376e+03 4.7298781e-24 +1.8699398e+03 4.7401785e-24 +1.8742519e+03 4.7505042e-24 +1.8785739e+03 4.7608553e-24 +1.8829059e+03 4.7712319e-24 +1.8872479e+03 4.7816339e-24 +1.8915999e+03 4.7920614e-24 +1.8959620e+03 4.8025145e-24 +1.9003341e+03 4.8129933e-24 +1.9047163e+03 4.8234978e-24 +1.9091086e+03 4.8340280e-24 +1.9135110e+03 4.8445840e-24 +1.9179235e+03 4.8551659e-24 +1.9223463e+03 4.8657737e-24 +1.9267792e+03 4.8764075e-24 +1.9312224e+03 4.8870673e-24 +1.9356758e+03 4.8977532e-24 +1.9401395e+03 4.9084653e-24 +1.9446135e+03 4.9192036e-24 +1.9490978e+03 4.9299682e-24 +1.9535924e+03 4.9407591e-24 +1.9580974e+03 4.9515766e-24 +1.9626128e+03 4.9624207e-24 +1.9671386e+03 4.9732916e-24 +1.9716748e+03 4.9841902e-24 +1.9762215e+03 4.9951190e-24 +1.9807787e+03 5.0060964e-24 +1.9853464e+03 5.0248458e-24 +1.9899246e+03 5.0280883e-24 +1.9945134e+03 5.0390799e-24 +1.9991128e+03 5.0501304e-24 +2.0037227e+03 5.0612121e-24 +2.0083433e+03 5.0723221e-24 +2.0129746e+03 5.0834596e-24 +2.0176165e+03 5.0946244e-24 +2.0222692e+03 5.1058165e-24 +2.0269325e+03 5.1170359e-24 +2.0316067e+03 5.1282827e-24 +2.0362916e+03 5.1395568e-24 +2.0409873e+03 5.1508584e-24 +2.0456938e+03 5.1621875e-24 +2.0504112e+03 5.1735441e-24 +2.0551394e+03 5.1849283e-24 +2.0598786e+03 5.1963401e-24 +2.0646287e+03 5.2077797e-24 +2.0693898e+03 5.2192471e-24 +2.0741618e+03 5.2307423e-24 +2.0789448e+03 5.2422654e-24 +2.0837389e+03 5.2538164e-24 +2.0885440e+03 5.2653955e-24 +2.0933602e+03 5.2770026e-24 +2.0981875e+03 5.2886379e-24 +2.1030259e+03 5.3003014e-24 +2.1078755e+03 5.3119932e-24 +2.1127363e+03 5.3237132e-24 +2.1176083e+03 5.3354617e-24 +2.1224915e+03 5.3472386e-24 +2.1273860e+03 5.3590440e-24 +2.1322918e+03 5.3708780e-24 +2.1372088e+03 5.3827407e-24 +2.1421373e+03 5.3946320e-24 +2.1470771e+03 5.4065521e-24 +2.1520282e+03 5.4185010e-24 +2.1569908e+03 5.4304788e-24 +2.1619649e+03 5.4424855e-24 +2.1669504e+03 5.4545213e-24 +2.1719474e+03 5.4665861e-24 +2.1769559e+03 5.4786801e-24 +2.1819760e+03 5.4908033e-24 +2.1870076e+03 5.5029557e-24 +2.1920509e+03 5.5151375e-24 +2.1971058e+03 5.5273487e-24 +2.2021723e+03 5.5395893e-24 +2.2072505e+03 5.5518595e-24 +2.2123405e+03 5.5641592e-24 +2.2174421e+03 5.5764886e-24 +2.2225556e+03 5.5888477e-24 +2.2276808e+03 5.6012367e-24 +2.2328179e+03 5.6136554e-24 +2.2379668e+03 5.6261041e-24 +2.2431275e+03 5.6385827e-24 +2.2483002e+03 5.6510914e-24 +2.2534848e+03 5.6636302e-24 +2.2586814e+03 5.6761992e-24 +2.2638899e+03 5.6887985e-24 +2.2691104e+03 5.7014280e-24 +2.2743430e+03 5.7140879e-24 +2.2795877e+03 5.7267783e-24 +2.2848444e+03 5.7394992e-24 +2.2901133e+03 5.7522506e-24 +2.2953943e+03 5.7650327e-24 +2.3006875e+03 5.7778456e-24 +2.3059929e+03 5.7906892e-24 +2.3113105e+03 5.8035636e-24 +2.3166404e+03 5.8164690e-24 +2.3219826e+03 5.8294053e-24 +2.3273371e+03 5.8423728e-24 +2.3327040e+03 5.8553713e-24 +2.3380832e+03 5.8684010e-24 +2.3434749e+03 5.8814620e-24 +2.3488789e+03 5.8945543e-24 +2.3542955e+03 5.9076780e-24 +2.3597245e+03 5.9208332e-24 +2.3651660e+03 5.9340199e-24 +2.3706201e+03 5.9472382e-24 +2.3760868e+03 5.9604882e-24 +2.3815661e+03 5.9737699e-24 +2.3870580e+03 5.9870835e-24 +2.3925626e+03 6.0004289e-24 +2.3980798e+03 6.0138063e-24 +2.4036098e+03 6.0272158e-24 +2.4091526e+03 6.0406573e-24 +2.4147081e+03 6.0541310e-24 +2.4202764e+03 6.0676369e-24 +2.4258576e+03 6.0811752e-24 +2.4314516e+03 6.0947459e-24 +2.4370586e+03 6.1083490e-24 +2.4426785e+03 6.1219846e-24 +2.4483113e+03 6.1356529e-24 +2.4539571e+03 6.1493539e-24 +2.4596160e+03 6.1630878e-24 +2.4652879e+03 6.1768548e-24 +2.4709728e+03 6.1906559e-24 +2.4766709e+03 6.2044980e-24 +2.4823821e+03 6.2220683e-24 +2.4881065e+03 6.2322589e-24 +2.4938441e+03 6.2461830e-24 +2.4995949e+03 6.2601464e-24 +2.5053590e+03 6.2741441e-24 +2.5111364e+03 6.2881754e-24 +2.5169271e+03 6.3022404e-24 +2.5227312e+03 6.3163389e-24 +2.5285486e+03 6.3304711e-24 +2.5343794e+03 6.3446370e-24 +2.5402237e+03 6.3588366e-24 +2.5460815e+03 6.3730702e-24 +2.5519528e+03 6.3873376e-24 +2.5578376e+03 6.4016391e-24 +2.5637360e+03 6.4159747e-24 +2.5696480e+03 6.4303444e-24 +2.5755736e+03 6.4447483e-24 +2.5815129e+03 6.4591865e-24 +2.5874659e+03 6.4736591e-24 +2.5934326e+03 6.4881662e-24 +2.5994131e+03 6.5027078e-24 +2.6054074e+03 6.5172840e-24 +2.6114155e+03 6.5318949e-24 +2.6174374e+03 6.5465405e-24 +2.6234732e+03 6.5612210e-24 +2.6295230e+03 6.5759365e-24 +2.6355867e+03 6.5906869e-24 +2.6416644e+03 6.6054724e-24 +2.6477561e+03 6.6202930e-24 +2.6538618e+03 6.6351489e-24 +2.6599816e+03 6.6500400e-24 +2.6661156e+03 6.6649666e-24 +2.6722637e+03 6.6799286e-24 +2.6784259e+03 6.6949262e-24 +2.6846024e+03 6.7099594e-24 +2.6907931e+03 6.7250283e-24 +2.6969981e+03 6.7401330e-24 +2.7032174e+03 6.7552736e-24 +2.7094510e+03 6.7704501e-24 +2.7156991e+03 6.7856626e-24 +2.7219615e+03 6.8009112e-24 +2.7282383e+03 6.8161960e-24 +2.7345297e+03 6.8315171e-24 +2.7408355e+03 6.8468745e-24 +2.7471559e+03 6.8622684e-24 +2.7534909e+03 6.8776987e-24 +2.7598405e+03 6.8931657e-24 +2.7662047e+03 6.9086693e-24 +2.7725836e+03 6.9242097e-24 +2.7789772e+03 6.9397869e-24 +2.7853855e+03 6.9554010e-24 +2.7918086e+03 6.9710522e-24 +2.7982465e+03 6.9867404e-24 +2.8046993e+03 7.0024658e-24 +2.8111670e+03 7.0182284e-24 +2.8176496e+03 7.0340284e-24 +2.8241471e+03 7.0498657e-24 +2.8306596e+03 7.0657406e-24 +2.8371871e+03 7.0816531e-24 +2.8437297e+03 7.0976032e-24 +2.8502873e+03 7.1135911e-24 +2.8568601e+03 7.1296169e-24 +2.8634481e+03 7.1456805e-24 +2.8700512e+03 7.1617822e-24 +2.8766696e+03 7.1779219e-24 +2.8833032e+03 7.1940999e-24 +2.8899521e+03 7.2103161e-24 +2.8966164e+03 7.2265706e-24 +2.9032960e+03 7.2428636e-24 +2.9099910e+03 7.2591951e-24 +2.9167015e+03 7.2755652e-24 +2.9234274e+03 7.2919740e-24 +2.9301688e+03 7.3084216e-24 +2.9369258e+03 7.3249081e-24 +2.9436984e+03 7.3414336e-24 +2.9504866e+03 7.3579981e-24 +2.9572904e+03 7.3746018e-24 +2.9641100e+03 7.3912451e-24 +2.9709452e+03 7.4079296e-24 +2.9777963e+03 7.4251142e-24 +2.9846631e+03 7.4414134e-24 +2.9915457e+03 7.4582104e-24 +2.9984443e+03 7.4750500e-24 +3.0053587e+03 7.4919298e-24 +3.0122891e+03 7.5088495e-24 +3.0192354e+03 7.5258091e-24 +3.0261978e+03 7.5428089e-24 +3.0331763e+03 7.5598487e-24 +3.0401708e+03 7.5769287e-24 +3.0471814e+03 7.5940490e-24 +3.0542083e+03 7.6112098e-24 +3.0612513e+03 7.6284109e-24 +3.0683106e+03 7.6456527e-24 +3.0753861e+03 7.6629351e-24 +3.0824780e+03 7.6802582e-24 +3.0895862e+03 7.6976222e-24 +3.0967108e+03 7.7150271e-24 +3.1038518e+03 7.7324730e-24 +3.1110093e+03 7.7499600e-24 +3.1181833e+03 7.7674882e-24 +3.1253739e+03 7.7850578e-24 +3.1325810e+03 7.8026687e-24 +3.1398048e+03 7.8203211e-24 +3.1470452e+03 7.8380151e-24 +3.1543023e+03 7.8557507e-24 +3.1615762e+03 7.8735281e-24 +3.1688668e+03 7.8913474e-24 +3.1761742e+03 7.9092086e-24 +3.1834985e+03 7.9271118e-24 +3.1908397e+03 7.9450572e-24 +3.1981978e+03 7.9630449e-24 +3.2055728e+03 7.9810749e-24 +3.2129649e+03 7.9991472e-24 +3.2203740e+03 8.0172622e-24 +3.2278002e+03 8.0354197e-24 +3.2352436e+03 8.0536199e-24 +3.2427041e+03 8.0718630e-24 +3.2501818e+03 8.0901490e-24 +3.2576767e+03 8.1084779e-24 +3.2651889e+03 8.1268500e-24 +3.2727185e+03 8.1452653e-24 +3.2802654e+03 8.1637238e-24 +3.2878297e+03 8.1822258e-24 +3.2954115e+03 8.2007712e-24 +3.3030107e+03 8.2193602e-24 +3.3106275e+03 8.2379930e-24 +3.3182618e+03 8.2566695e-24 +3.3259138e+03 8.2753899e-24 +3.3335833e+03 8.2941542e-24 +3.3412706e+03 8.3129627e-24 +3.3489756e+03 8.3318153e-24 +3.3566984e+03 8.3507123e-24 +3.3644389e+03 8.3696536e-24 +3.3721974e+03 8.3886393e-24 +3.3799737e+03 8.4076697e-24 +3.3877679e+03 8.4267448e-24 +3.3955801e+03 8.4458646e-24 +3.4034104e+03 8.4650293e-24 +3.4112587e+03 8.4842391e-24 +3.4191251e+03 8.5034939e-24 +3.4270096e+03 8.5227939e-24 +3.4349123e+03 8.5421392e-24 +3.4428332e+03 8.5615299e-24 +3.4507724e+03 8.5809661e-24 +3.4587299e+03 8.6004481e-24 +3.4667058e+03 8.6199767e-24 +3.4747000e+03 8.7186825e-24 +3.4827127e+03 8.6591692e-24 +3.4907439e+03 8.6788333e-24 +3.4987936e+03 8.6985444e-24 +3.5068618e+03 8.7183019e-24 +3.5149487e+03 8.7381058e-24 +3.5230542e+03 8.7579561e-24 +3.5311784e+03 8.7778529e-24 +3.5393213e+03 8.7977964e-24 +3.5474830e+03 8.8177867e-24 +3.5556635e+03 8.8378238e-24 +3.5638629e+03 8.8579078e-24 +3.5720812e+03 8.8780390e-24 +3.5803184e+03 8.8982173e-24 +3.5885747e+03 8.9184429e-24 +3.5968500e+03 8.9387158e-24 +3.6051443e+03 8.9590363e-24 +3.6134578e+03 8.9794044e-24 +3.6217905e+03 8.9998202e-24 +3.6301424e+03 9.0202838e-24 +3.6385135e+03 9.0407953e-24 +3.6469039e+03 9.0613549e-24 +3.6553137e+03 9.0819626e-24 +3.6637429e+03 9.1026185e-24 +3.6721915e+03 9.1233229e-24 +3.6806596e+03 9.1440757e-24 +3.6891472e+03 9.1648770e-24 +3.6976544e+03 9.1857271e-24 +3.7061813e+03 9.2066260e-24 +3.7147277e+03 9.2275738e-24 +3.7232939e+03 9.2485706e-24 +3.7318799e+03 9.2696166e-24 +3.7404856e+03 9.2907118e-24 +3.7491112e+03 9.3118563e-24 +3.7577567e+03 9.3330504e-24 +3.7664221e+03 9.3542940e-24 +3.7751075e+03 9.3755873e-24 +3.7838129e+03 9.3969304e-24 +3.7925384e+03 9.4183234e-24 +3.8012841e+03 9.4397665e-24 +3.8100498e+03 9.4612597e-24 +3.8188359e+03 9.4828032e-24 +3.8276421e+03 9.5043971e-24 +3.8364687e+03 9.5260414e-24 +3.8453156e+03 9.5477364e-24 +3.8541829e+03 9.5694821e-24 +3.8630707e+03 9.5912786e-24 +3.8719790e+03 9.6131260e-24 +3.8809078e+03 9.6350246e-24 +3.8898572e+03 9.6569743e-24 +3.8988272e+03 9.6789753e-24 +3.9078180e+03 9.7010277e-24 +3.9168294e+03 9.7231317e-24 +3.9258617e+03 9.7452873e-24 +3.9349147e+03 9.7674947e-24 +3.9439887e+03 9.7897540e-24 +3.9530835e+03 9.8120654e-24 +3.9621994e+03 9.8344293e-24 +3.9713362e+03 9.8573889e-24 +3.9804942e+03 9.8793128e-24 +3.9896732e+03 9.9018328e-24 +3.9988734e+03 9.9244057e-24 +4.0080949e+03 9.9470315e-24 +4.0173376e+03 9.9697101e-24 +4.0266016e+03 9.9924416e-24 +4.0358870e+03 1.0015226e-23 +4.0451937e+03 1.0038064e-23 +4.0545220e+03 1.0060955e-23 +4.0638718e+03 1.0083900e-23 +4.0732431e+03 1.0106898e-23 +4.0826360e+03 1.0129950e-23 +4.0920506e+03 1.0153055e-23 +4.1014869e+03 1.0176215e-23 +4.1109450e+03 1.0199429e-23 +4.1204248e+03 1.0222696e-23 +4.1299266e+03 1.0246018e-23 +4.1394502e+03 1.0269395e-23 +4.1489958e+03 1.0292826e-23 +4.1585634e+03 1.0316312e-23 +4.1681531e+03 1.0339852e-23 +4.1777649e+03 1.0363448e-23 +4.1873988e+03 1.0387098e-23 +4.1970550e+03 1.0410804e-23 +4.2067335e+03 1.0434565e-23 +4.2164342e+03 1.0458381e-23 +4.2261574e+03 1.0482253e-23 +4.2359029e+03 1.0506180e-23 +4.2456709e+03 1.0530164e-23 +4.2554615e+03 1.0554203e-23 +4.2652746e+03 1.0578298e-23 +4.2751104e+03 1.0602450e-23 +4.2849688e+03 1.0626658e-23 +4.2948500e+03 1.0650922e-23 +4.3047539e+03 1.0675243e-23 +4.3146807e+03 1.0699620e-23 +4.3246304e+03 1.0724054e-23 +4.3346030e+03 1.0748546e-23 +4.3445987e+03 1.0773094e-23 +4.3546173e+03 1.0797700e-23 +4.3646591e+03 1.0822363e-23 +4.3747241e+03 1.0847083e-23 +4.3848122e+03 1.0871861e-23 +4.3949236e+03 1.0896697e-23 +4.4050583e+03 1.0921590e-23 +4.4152164e+03 1.0946542e-23 +4.4253980e+03 1.0971552e-23 +4.4356030e+03 1.0996620e-23 +4.4458315e+03 1.1021746e-23 +4.4560836e+03 1.1046931e-23 +4.4663594e+03 1.1072218e-23 +4.4766588e+03 1.1097477e-23 +4.4869821e+03 1.1122838e-23 +4.4973291e+03 1.1148258e-23 +4.5076999e+03 1.1173738e-23 +4.5180947e+03 1.1199277e-23 +4.5285135e+03 1.1224875e-23 +4.5389563e+03 1.1250533e-23 +4.5494231e+03 1.1276251e-23 +4.5599142e+03 1.1302029e-23 +4.5704294e+03 1.1327866e-23 +4.5809688e+03 1.1353764e-23 +4.5915325e+03 1.1379722e-23 +4.6021207e+03 1.1405740e-23 +4.6127332e+03 1.1431820e-23 +4.6233702e+03 1.1457959e-23 +4.6340317e+03 1.1484160e-23 +4.6447178e+03 1.1510421e-23 +4.6554286e+03 1.1536744e-23 +4.6661640e+03 1.1563128e-23 +4.6769242e+03 1.1589573e-23 +4.6877093e+03 1.1616080e-23 +4.6985192e+03 1.1642649e-23 +4.7093540e+03 1.1669279e-23 +4.7202138e+03 1.1695972e-23 +4.7310986e+03 1.1722726e-23 +4.7420086e+03 1.1749543e-23 +4.7529437e+03 1.1776422e-23 +4.7639040e+03 1.1803363e-23 +4.7748896e+03 1.1830368e-23 +4.7859005e+03 1.1857435e-23 +4.7969369e+03 1.1884565e-23 +4.8079986e+03 1.1911758e-23 +4.8190859e+03 1.1939014e-23 +4.8301988e+03 1.1966334e-23 +4.8413372e+03 1.1993717e-23 +4.8525014e+03 1.2021164e-23 +4.8636913e+03 1.2048675e-23 +4.8749070e+03 1.2076250e-23 +4.8861486e+03 1.2103889e-23 +4.8974161e+03 1.2131592e-23 +4.9087096e+03 1.2159360e-23 +4.9200291e+03 1.2187192e-23 +4.9313747e+03 1.2215089e-23 +4.9427465e+03 1.2243051e-23 +4.9541445e+03 1.2271077e-23 +4.9655688e+03 1.2299174e-23 +4.9770194e+03 1.2327326e-23 +4.9884965e+03 1.2355549e-23 +5.0000000e+03 1.2383837e-23 diff --git a/bilby/gw/noise_curves/highf_psd.txt b/bilby/gw/detector/noise_curves/highf_psd.txt similarity index 100% rename from bilby/gw/noise_curves/highf_psd.txt rename to bilby/gw/detector/noise_curves/highf_psd.txt diff --git a/bilby/gw/noise_curves/lisa_asd.txt b/bilby/gw/detector/noise_curves/lisa_asd.txt similarity index 100% rename from bilby/gw/noise_curves/lisa_asd.txt rename to bilby/gw/detector/noise_curves/lisa_asd.txt diff --git a/bilby/gw/noise_curves/lisa_psd.txt b/bilby/gw/detector/noise_curves/lisa_psd.txt similarity index 100% rename from bilby/gw/noise_curves/lisa_psd.txt rename to bilby/gw/detector/noise_curves/lisa_psd.txt diff --git a/bilby/gw/detector/psd.py b/bilby/gw/detector/psd.py new file mode 100644 index 0000000000000000000000000000000000000000..a0d30d6a49e5d1b587f35514bdad3e3d7eb74991 --- /dev/null +++ b/bilby/gw/detector/psd.py @@ -0,0 +1,267 @@ +import os + +import numpy as np +from scipy.interpolate import interp1d + +from bilby.core import utils +from bilby.core.utils import logger +from .strain_data import InterferometerStrainData + + +class PowerSpectralDensity(object): + + def __init__(self, frequency_array=None, psd_array=None, asd_array=None, + psd_file=None, asd_file=None): + """ + Instantiate a new PowerSpectralDensity object. + + Example + ------- + Using the `from` method directly (here `psd_file` is a string + containing the path to the file to load): + >>> power_spectral_density = PowerSpectralDensity.from_power_spectral_density_file(psd_file) + + Alternatively (and equivalently) setting the psd_file directly: + >>> power_spectral_density = PowerSpectralDensity(psd_file=psd_file) + + Attributes + ---------- + asd_array: array_like + Array representation of the ASD + asd_file: str + Name of the ASD file + frequency_array: array_like + Array containing the frequencies of the ASD/PSD values + psd_array: array_like + Array representation of the PSD + psd_file: str + Name of the PSD file + power_spectral_density_interpolated: scipy.interpolated.interp1d + Interpolated function of the PSD + + """ + self.frequency_array = np.array(frequency_array) + if psd_array is not None: + self.psd_array = psd_array + if asd_array is not None: + self.asd_array = asd_array + self.psd_file = psd_file + self.asd_file = asd_file + + def __eq__(self, other): + if self.psd_file == other.psd_file \ + and self.asd_file == other.asd_file \ + and np.array_equal(self.frequency_array, other.frequency_array) \ + and np.array_equal(self.psd_array, other.psd_array) \ + and np.array_equal(self.asd_array, other.asd_array): + return True + return False + + def __repr__(self): + if self.asd_file is not None or self.psd_file is not None: + return self.__class__.__name__ + '(psd_file=\'{}\', asd_file=\'{}\')' \ + .format(self.psd_file, self.asd_file) + else: + return self.__class__.__name__ + '(frequency_array={}, psd_array={}, asd_array={})' \ + .format(self.frequency_array, self.psd_array, self.asd_array) + + @staticmethod + def from_amplitude_spectral_density_file(asd_file): + """ Set the amplitude spectral density from a given file + + Parameters + ---------- + asd_file: str + File containing amplitude spectral density, format 'f h_f' + + """ + return PowerSpectralDensity(asd_file=asd_file) + + @staticmethod + def from_power_spectral_density_file(psd_file): + """ Set the power spectral density from a given file + + Parameters + ---------- + psd_file: str, optional + File containing power spectral density, format 'f h_f' + + """ + return PowerSpectralDensity(psd_file=psd_file) + + @staticmethod + def from_frame_file(frame_file, psd_start_time, psd_duration, + fft_length=4, sampling_frequency=4096, roll_off=0.2, + overlap=0, channel=None, name=None, outdir=None, + analysis_segment_start_time=None): + """ Generate power spectral density from a frame file + + Parameters + ---------- + frame_file: str, optional + Frame file to read data from. + psd_start_time: float + Beginning of segment to analyse. + psd_duration: float, optional + Duration of data (in seconds) to generate PSD from. + fft_length: float, optional + Number of seconds in a single fft. + sampling_frequency: float, optional + Sampling frequency for time series. + This is twice the maximum frequency. + roll_off: float, optional + Rise time in seconds of tukey window. + overlap: float, + Number of seconds of overlap between FFTs. + channel: str, optional + Name of channel to use to generate PSD. + name, outdir: str, optional + Name (and outdir) of the detector for which a PSD is to be + generated. + analysis_segment_start_time: float, optional + The start time of the analysis segment, if given, this data will + be removed before creating the PSD. + + """ + strain = InterferometerStrainData(roll_off=roll_off) + strain.set_from_frame_file( + frame_file, start_time=psd_start_time, duration=psd_duration, + channel=channel, sampling_frequency=sampling_frequency) + frequency_array, psd_array = strain.create_power_spectral_density( + fft_length=fft_length, name=name, outdir=outdir, overlap=overlap, + analysis_segment_start_time=analysis_segment_start_time) + return PowerSpectralDensity(frequency_array=frequency_array, psd_array=psd_array) + + @staticmethod + def from_amplitude_spectral_density_array(frequency_array, asd_array): + return PowerSpectralDensity(frequency_array=frequency_array, asd_array=asd_array) + + @staticmethod + def from_power_spectral_density_array(frequency_array, psd_array): + return PowerSpectralDensity(frequency_array=frequency_array, psd_array=psd_array) + + @staticmethod + def from_aligo(): + logger.info("No power spectral density provided, using aLIGO," + "zero detuning, high power.") + return PowerSpectralDensity.from_power_spectral_density_file(psd_file='aLIGO_ZERO_DET_high_P_psd.txt') + + @property + def psd_array(self): + return self.__psd_array + + @psd_array.setter + def psd_array(self, psd_array): + self.__check_frequency_array_matches_density_array(psd_array) + self.__psd_array = np.array(psd_array) + self.__asd_array = psd_array ** 0.5 + self.__interpolate_power_spectral_density() + + @property + def asd_array(self): + return self.__asd_array + + @asd_array.setter + def asd_array(self, asd_array): + self.__check_frequency_array_matches_density_array(asd_array) + self.__asd_array = np.array(asd_array) + self.__psd_array = asd_array ** 2 + self.__interpolate_power_spectral_density() + + def __check_frequency_array_matches_density_array(self, density_array): + if len(self.frequency_array) != len(density_array): + raise ValueError('Provided spectral density does not match frequency array. Not updating.\n' + 'Length spectral density {}\n Length frequency array {}\n' + .format(density_array, self.frequency_array)) + + def __interpolate_power_spectral_density(self): + """Interpolate the loaded power spectral density so it can be resampled + for arbitrary frequency arrays. + """ + self.__power_spectral_density_interpolated = interp1d(self.frequency_array, + self.psd_array, + bounds_error=False, + fill_value=np.inf) + + @property + def power_spectral_density_interpolated(self): + return self.__power_spectral_density_interpolated + + @property + def asd_file(self): + return self._asd_file + + @asd_file.setter + def asd_file(self, asd_file): + asd_file = self.__validate_file_name(file=asd_file) + self._asd_file = asd_file + if asd_file is not None: + self.__import_amplitude_spectral_density() + self.__check_file_was_asd_file() + + def __check_file_was_asd_file(self): + if min(self.asd_array) < 1e-30: + logger.warning("You specified an amplitude spectral density file.") + logger.warning("{} WARNING {}".format("*" * 30, "*" * 30)) + logger.warning("The minimum of the provided curve is {:.2e}.".format(min(self.asd_array))) + logger.warning("You may have intended to provide this as a power spectral density.") + + @property + def psd_file(self): + return self._psd_file + + @psd_file.setter + def psd_file(self, psd_file): + psd_file = self.__validate_file_name(file=psd_file) + self._psd_file = psd_file + if psd_file is not None: + self.__import_power_spectral_density() + self.__check_file_was_psd_file() + + def __check_file_was_psd_file(self): + if min(self.psd_array) > 1e-30: + logger.warning("You specified a power spectral density file.") + logger.warning("{} WARNING {}".format("*" * 30, "*" * 30)) + logger.warning("The minimum of the provided curve is {:.2e}.".format(min(self.psd_array))) + logger.warning("You may have intended to provide this as an amplitude spectral density.") + + @staticmethod + def __validate_file_name(file): + """ + Test if the file contains a path (i.e., contains '/'). + If not assume the file is in the default directory. + """ + if file is not None and '/' not in file: + file = os.path.join(os.path.dirname(__file__), 'noise_curves', file) + return file + + def __import_amplitude_spectral_density(self): + """ Automagically load an amplitude spectral density curve """ + self.frequency_array, self.asd_array = np.genfromtxt(self.asd_file).T + + def __import_power_spectral_density(self): + """ Automagically load a power spectral density curve """ + self.frequency_array, self.psd_array = np.genfromtxt(self.psd_file).T + + def get_noise_realisation(self, sampling_frequency, duration): + """ + Generate frequency Gaussian noise scaled to the power spectral density. + + Parameters + ------- + sampling_frequency: float + sampling frequency of noise + duration: float + duration of noise + + Returns + ------- + array_like: frequency domain strain of this noise realisation + array_like: frequencies related to the frequency domain strain + + """ + white_noise, frequencies = utils.create_white_noise(sampling_frequency, duration) + frequency_domain_strain = self.__power_spectral_density_interpolated(frequencies) ** 0.5 * white_noise + out_of_bounds = (frequencies < min(self.frequency_array)) | (frequencies > max(self.frequency_array)) + frequency_domain_strain[out_of_bounds] = 0 * (1 + 1j) + return frequency_domain_strain, frequencies diff --git a/bilby/gw/detector/strain_data.py b/bilby/gw/detector/strain_data.py new file mode 100644 index 0000000000000000000000000000000000000000..e68083d8490e6bcea6f9f7212f05a15f7546e0e0 --- /dev/null +++ b/bilby/gw/detector/strain_data.py @@ -0,0 +1,688 @@ +import numpy as np +from scipy.signal.windows import tukey + +from bilby.core import utils +from bilby.core.series import CoupledTimeAndFrequencySeries +from bilby.core.utils import logger +from bilby.gw import utils as gwutils + +try: + import gwpy + import gwpy.signal +except ImportError: + logger.warning("You do not have gwpy installed currently. You will " + " not be able to use some of the prebuilt functions.") + +try: + import lal +except ImportError: + logger.warning("You do not have lalsuite installed currently. You will" + " not be able to use some of the prebuilt functions.") + + +class InterferometerStrainData(object): + """ Strain data for an interferometer """ + + def __init__(self, minimum_frequency=0, maximum_frequency=np.inf, + roll_off=0.2): + """ Initiate an InterferometerStrainData object + + The initialised object contains no data, this should be added using one + of the `set_from..` methods. + + Parameters + ---------- + minimum_frequency: float + Minimum frequency to analyse for detector. Default is 0. + maximum_frequency: float + Maximum frequency to analyse for detector. Default is infinity. + roll_off: float + The roll-off (in seconds) used in the Tukey window, default=0.2s. + This corresponds to alpha * duration / 2 for scipy tukey window. + + """ + self.minimum_frequency = minimum_frequency + self.maximum_frequency = maximum_frequency + self.roll_off = roll_off + self.window_factor = 1 + + self._times_and_frequencies = CoupledTimeAndFrequencySeries() + # self._set_time_and_frequency_array_parameters(None, None, None) + + self._frequency_domain_strain = None + self._frequency_array = None + self._time_domain_strain = None + self._time_array = None + self._channel = None + + def __eq__(self, other): + if self.minimum_frequency == other.minimum_frequency \ + and self.maximum_frequency == other.maximum_frequency \ + and self.roll_off == other.roll_off \ + and self.window_factor == other.window_factor \ + and self.sampling_frequency == other.sampling_frequency \ + and self.duration == other.duration \ + and self.start_time == other.start_time \ + and np.array_equal(self.time_array, other.time_array) \ + and np.array_equal(self.frequency_array, other.frequency_array) \ + and np.array_equal(self.frequency_domain_strain, other.frequency_domain_strain) \ + and np.array_equal(self.time_domain_strain, other.time_domain_strain): + return True + return False + + def time_within_data(self, time): + """ Check if time is within the data span + + Parameters + ---------- + time: float + The time to check + + Returns + ------- + bool: + A boolean stating whether the time is inside or outside the span + + """ + if time < self.start_time: + logger.debug("Time is before the start_time") + return False + elif time > self.start_time + self.duration: + logger.debug("Time is after the start_time + duration") + return False + else: + return True + + @property + def minimum_frequency(self): + return self.__minimum_frequency + + @minimum_frequency.setter + def minimum_frequency(self, minimum_frequency): + self.__minimum_frequency = minimum_frequency + + @property + def maximum_frequency(self): + """ Force the maximum frequency be less than the Nyquist frequency """ + if self.sampling_frequency is not None: + if 2 * self.__maximum_frequency > self.sampling_frequency: + self.__maximum_frequency = self.sampling_frequency / 2. + return self.__maximum_frequency + + @maximum_frequency.setter + def maximum_frequency(self, maximum_frequency): + self.__maximum_frequency = maximum_frequency + + @property + def frequency_mask(self): + """Masking array for limiting the frequency band. + + Returns + ------- + array_like: An array of boolean values + """ + try: + return self._frequency_mask + except AttributeError: + frequency_array = self._times_and_frequencies.frequency_array + mask = ((frequency_array >= self.minimum_frequency) & + (frequency_array <= self.maximum_frequency)) + self._frequency_mask = mask + return self._frequency_mask + + @property + def alpha(self): + return 2 * self.roll_off / self.duration + + def time_domain_window(self, roll_off=None, alpha=None): + """ + Window function to apply to time domain data before FFTing. + + This defines self.window_factor as the power loss due to the windowing. + See https://dcc.ligo.org/DocDB/0027/T040089/000/T040089-00.pdf + + Parameters + ---------- + roll_off: float + Rise time of window in seconds + alpha: float + Parameter to pass to tukey window, how much of segment falls + into windowed part + + Return + ------ + window: array + Window function over time array + """ + if roll_off is not None: + self.roll_off = roll_off + elif alpha is not None: + self.roll_off = alpha * self.duration / 2 + window = tukey(len(self._time_domain_strain), alpha=self.alpha) + self.window_factor = np.mean(window ** 2) + return window + + @property + def time_domain_strain(self): + """ The time domain strain, in units of strain """ + if self._time_domain_strain is not None: + return self._time_domain_strain + elif self._frequency_domain_strain is not None: + self._time_domain_strain = utils.infft( + self.frequency_domain_strain, self.sampling_frequency) + return self._time_domain_strain + + else: + raise ValueError("time domain strain data not yet set") + + @property + def frequency_domain_strain(self): + """ Returns the frequency domain strain + + This is the frequency domain strain normalised to units of + strain / Hz, obtained by a one-sided Fourier transform of the + time domain data, divided by the sampling frequency. + """ + if self._frequency_domain_strain is not None: + return self._frequency_domain_strain * self.frequency_mask + elif self._time_domain_strain is not None: + logger.info("Generating frequency domain strain from given time " + "domain strain.") + logger.info("Applying a tukey window with alpha={}, roll off={}".format( + self.alpha, self.roll_off)) + # self.low_pass_filter() + window = self.time_domain_window() + self._frequency_domain_strain, self.frequency_array = utils.nfft( + self._time_domain_strain * window, self.sampling_frequency) + return self._frequency_domain_strain * self.frequency_mask + else: + raise ValueError("frequency domain strain data not yet set") + + @frequency_domain_strain.setter + def frequency_domain_strain(self, frequency_domain_strain): + if not len(self.frequency_array) == len(frequency_domain_strain): + raise ValueError("The frequency_array and the set strain have different lengths") + self._frequency_domain_strain = frequency_domain_strain + + def to_gwpy_timeseries(self): + """ + Output the time series strain data as a :class:`gwpy.timeseries.TimeSeries`. + """ + + return gwpy.timeseries.TimeSeries(self.time_domain_strain, + sample_rate=self.sampling_frequency, + t0=self.start_time, + channel=self.channel) + + def to_pycbc_timeseries(self): + """ + Output the time series strain data as a :class:`pycbc.types.timeseries.TimeSeries`. + """ + + try: + import pycbc + except ImportError: + raise ImportError("Cannot output strain data as PyCBC TimeSeries") + + return pycbc.types.timeseries.TimeSeries(self.time_domain_strain, + delta_t=(1. / self.sampling_frequency), + epoch=lal.LIGOTimeGPS(self.start_time)) + + def to_lal_timeseries(self): + """ + Output the time series strain data as a LAL TimeSeries object. + """ + + laldata = lal.CreateREAL8TimeSeries("", + lal.LIGOTimeGPS(self.start_time), + 0., (1. / self.sampling_frequency), + lal.SecondUnit, + len(self.time_domain_strain)) + laldata.data.data[:] = self.time_domain_strain + + return laldata + + def to_gwpy_frequencyseries(self): + """ + Output the frequency series strain data as a :class:`gwpy.frequencyseries.FrequencySeries`. + """ + + return gwpy.frequencyseries.FrequencySeries(self.frequency_domain_strain, + frequencies=self.frequency_array, + epoch=self.start_time, + channel=self.channel) + + def to_pycbc_frequencyseries(self): + """ + Output the frequency series strain data as a :class:`pycbc.types.frequencyseries.FrequencySeries`. + """ + + try: + import pycbc + except ImportError: + raise ImportError("Cannot output strain data as PyCBC FrequencySeries") + + return pycbc.types.frequencyseries.FrequencySeries(self.frequency_domain_strain, + delta_f=(self.frequency_array[1] - self.frequency_array[0]), + epoch=lal.LIGOTimeGPS(self.start_time)) + + def to_lal_frequencyseries(self): + """ + Output the frequency series strain data as a LAL FrequencySeries object. + """ + + laldata = lal.CreateCOMPLEX16FrequencySeries("", + lal.LIGOTimeGPS(self.start_time), + self.frequency_array[0], + (self.frequency_array[1] - self.frequency_array[0]), + lal.SecondUnit, + len(self.frequency_domain_strain)) + laldata.data.data[:] = self.frequency_domain_strain + + return laldata + + def add_to_frequency_domain_strain(self, x): + """Deprecated""" + self._frequency_domain_strain += x + + def low_pass_filter(self, filter_freq=None): + """ Low pass filter the data """ + + if filter_freq is None: + logger.debug( + "Setting low pass filter_freq using given maximum frequency") + filter_freq = self.maximum_frequency + + if 2 * filter_freq >= self.sampling_frequency: + logger.info( + "Low pass filter frequency of {}Hz requested, this is equal" + " or greater than the Nyquist frequency so no filter applied" + .format(filter_freq)) + return + + logger.debug("Applying low pass filter with filter frequency {}".format(filter_freq)) + bp = gwpy.signal.filter_design.lowpass( + filter_freq, self.sampling_frequency) + strain = gwpy.timeseries.TimeSeries( + self.time_domain_strain, sample_rate=self.sampling_frequency) + strain = strain.filter(bp, filtfilt=True) + self._time_domain_strain = strain.value + + def create_power_spectral_density( + self, fft_length, overlap=0, name='unknown', outdir=None, + analysis_segment_start_time=None): + """ Use the time domain strain to generate a power spectral density + + This create a Tukey-windowed power spectral density and writes it to a + PSD file. + + Parameters + ---------- + fft_length: float + Duration of the analysis segment. + overlap: float + Number of seconds of overlap between FFTs. + name: str + The name of the detector, used in storing the PSD. Defaults to + "unknown". + outdir: str + The output directory to write the PSD file too. If not given, + the PSD will not be written to file. + analysis_segment_start_time: float + The start time of the analysis segment, if given, this data will + be removed before creating the PSD. + + Returns + ------- + frequency_array, psd : array_like + The frequencies and power spectral density array + + """ + + data = self.time_domain_strain + + if analysis_segment_start_time is not None: + analysis_segment_end_time = analysis_segment_start_time + fft_length + inside = (analysis_segment_start_time > self.time_array[0] + + analysis_segment_end_time < self.time_array[-1]) + if inside: + logger.info("Removing analysis segment data from the PSD data") + idxs = ( + (self.time_array < analysis_segment_start_time) + + (self.time_array > analysis_segment_end_time)) + data = data[idxs] + + # WARNING this line can cause issues if the data is non-contiguous + strain = gwpy.timeseries.TimeSeries(data=data, sample_rate=self.sampling_frequency) + psd_alpha = 2 * self.roll_off / fft_length + logger.info( + "Tukey window PSD data with alpha={}, roll off={}".format( + psd_alpha, self.roll_off)) + psd = strain.psd( + fftlength=fft_length, overlap=overlap, window=('tukey', psd_alpha)) + + if outdir: + psd_file = '{}/{}_PSD_{}_{}.txt'.format(outdir, name, self.start_time, self.duration) + with open('{}'.format(psd_file), 'w+') as opened_file: + for f, p in zip(psd.frequencies.value, psd.value): + opened_file.write('{} {}\n'.format(f, p)) + + return psd.frequencies.value, psd.value + + def _infer_time_domain_dependence( + self, start_time, sampling_frequency, duration, time_array): + """ Helper function to figure out if the time_array, or + sampling_frequency and duration where given + """ + self._infer_dependence(domain='time', array=time_array, duration=duration, + sampling_frequency=sampling_frequency, start_time=start_time) + + def _infer_frequency_domain_dependence( + self, start_time, sampling_frequency, duration, frequency_array): + """ Helper function to figure out if the frequency_array, or + sampling_frequency and duration where given + """ + + self._infer_dependence(domain='frequency', array=frequency_array, + duration=duration, sampling_frequency=sampling_frequency, start_time=start_time) + + def _infer_dependence(self, domain, array, duration, sampling_frequency, start_time): + if (sampling_frequency is not None) and (duration is not None): + if array is not None: + raise ValueError( + "You have given the sampling_frequency, duration, and " + "an array") + pass + elif array is not None: + if domain == 'time': + self.time_array = array + elif domain == 'frequency': + self.frequency_array = array + return + elif sampling_frequency is None or duration is None: + raise ValueError( + "You must provide both sampling_frequency and duration") + else: + raise ValueError( + "Insufficient information given to set arrays") + self._set_time_and_frequency_array_parameters(duration=duration, + sampling_frequency=sampling_frequency, + start_time=start_time) + + def set_from_time_domain_strain( + self, time_domain_strain, sampling_frequency=None, duration=None, + start_time=0, time_array=None): + """ Set the strain data from a time domain strain array + + This sets the time_domain_strain attribute, the frequency_domain_strain + is automatically calculated after a low-pass filter and Tukey window + is applied. + + Parameters + ---------- + time_domain_strain: array_like + An array of the time domain strain. + sampling_frequency: float + The sampling frequency (in Hz). + duration: float + The data duration (in s). + start_time: float + The GPS start-time of the data. + time_array: array_like + The array of times, if sampling_frequency and duration not + given. + + """ + self._infer_time_domain_dependence(start_time=start_time, + sampling_frequency=sampling_frequency, + duration=duration, + time_array=time_array) + + logger.debug('Setting data using provided time_domain_strain') + if np.shape(time_domain_strain) == np.shape(self.time_array): + self._time_domain_strain = time_domain_strain + self._frequency_domain_strain = None + else: + raise ValueError("Data times do not match time array") + + def set_from_gwpy_timeseries(self, time_series): + """ Set the strain data from a gwpy TimeSeries + + This sets the time_domain_strain attribute, the frequency_domain_strain + is automatically calculated after a low-pass filter and Tukey window + is applied. + + Parameters + ---------- + time_series: gwpy.timeseries.timeseries.TimeSeries + + """ + logger.debug('Setting data using provided gwpy TimeSeries object') + if type(time_series) != gwpy.timeseries.TimeSeries: + raise ValueError("Input time_series is not a gwpy TimeSeries") + self._set_time_and_frequency_array_parameters(duration=time_series.duration.value, + sampling_frequency=time_series.sample_rate.value, + start_time=time_series.epoch.value) + self._time_domain_strain = time_series.value + self._frequency_domain_strain = None + self._channel = time_series.channel + + @property + def channel(self): + return self._channel + + def set_from_open_data( + self, name, start_time, duration=4, outdir='outdir', cache=True, + **kwargs): + """ Set the strain data from open LOSC data + + This sets the time_domain_strain attribute, the frequency_domain_strain + is automatically calculated after a low-pass filter and Tukey window + is applied. + + Parameters + ---------- + name: str + Detector name, e.g., 'H1'. + start_time: float + Start GPS time of segment. + duration: float, optional + The total time (in seconds) to analyse. Defaults to 4s. + outdir: str + Directory where the psd files are saved + cache: bool, optional + Whether or not to store/use the acquired data. + **kwargs: + All keyword arguments are passed to + `gwpy.timeseries.TimeSeries.fetch_open_data()`. + + """ + + timeseries = gwutils.get_open_strain_data( + name, start_time, start_time + duration, outdir=outdir, cache=cache, + **kwargs) + + self.set_from_gwpy_timeseries(timeseries) + + def set_from_csv(self, filename): + """ Set the strain data from a csv file + + Parameters + ---------- + filename: str + The path to the file to read in + + """ + timeseries = gwpy.timeseries.TimeSeries.read(filename, format='csv') + self.set_from_gwpy_timeseries(timeseries) + + def set_from_frequency_domain_strain( + self, frequency_domain_strain, sampling_frequency=None, + duration=None, start_time=0, frequency_array=None): + """ Set the `frequency_domain_strain` from a numpy array + + Parameters + ---------- + frequency_domain_strain: array_like + The data to set. + sampling_frequency: float + The sampling frequency (in Hz). + duration: float + The data duration (in s). + start_time: float + The GPS start-time of the data. + frequency_array: array_like + The array of frequencies, if sampling_frequency and duration not + given. + + """ + + self._infer_frequency_domain_dependence(start_time=start_time, + sampling_frequency=sampling_frequency, + duration=duration, + frequency_array=frequency_array) + + logger.debug('Setting data using provided frequency_domain_strain') + if np.shape(frequency_domain_strain) == np.shape(self.frequency_array): + self._frequency_domain_strain = frequency_domain_strain + self.window_factor = 1 + else: + raise ValueError("Data frequencies do not match frequency_array") + + def set_from_power_spectral_density( + self, power_spectral_density, sampling_frequency, duration, + start_time=0): + """ Set the `frequency_domain_strain` by generating a noise realisation + + Parameters + ---------- + power_spectral_density: bilby.gw.detector.PowerSpectralDensity + A PowerSpectralDensity object used to generate the data + sampling_frequency: float + The sampling frequency (in Hz) + duration: float + The data duration (in s) + start_time: float + The GPS start-time of the data + + """ + + self._set_time_and_frequency_array_parameters(duration=duration, + sampling_frequency=sampling_frequency, + start_time=start_time) + + logger.debug( + 'Setting data using noise realization from provided' + 'power_spectal_density') + frequency_domain_strain, frequency_array = \ + power_spectral_density.get_noise_realisation( + self.sampling_frequency, self.duration) + + if np.array_equal(frequency_array, self.frequency_array): + self._frequency_domain_strain = frequency_domain_strain + else: + raise ValueError("Data frequencies do not match frequency_array") + + def set_from_zero_noise(self, sampling_frequency, duration, start_time=0): + """ Set the `frequency_domain_strain` to zero noise + + Parameters + ---------- + sampling_frequency: float + The sampling frequency (in Hz) + duration: float + The data duration (in s) + start_time: float + The GPS start-time of the data + + """ + + self._set_time_and_frequency_array_parameters(duration=duration, + sampling_frequency=sampling_frequency, + start_time=start_time) + + logger.debug('Setting zero noise data') + self._frequency_domain_strain = np.zeros_like(self.frequency_array, + dtype=np.complex) + + def set_from_frame_file( + self, frame_file, sampling_frequency, duration, start_time=0, + channel=None, buffer_time=1): + """ Set the `frequency_domain_strain` from a frame fiile + + Parameters + ---------- + frame_file: str + File from which to load data. + channel: str + Channel to read from frame. + sampling_frequency: float + The sampling frequency (in Hz) + duration: float + The data duration (in s) + start_time: float + The GPS start-time of the data + buffer_time: float + Read in data with `start_time-buffer_time` and + `start_time+duration+buffer_time` + + """ + + self._set_time_and_frequency_array_parameters(duration=duration, + sampling_frequency=sampling_frequency, + start_time=start_time) + + logger.info('Reading data from frame file {}'.format(frame_file)) + strain = gwutils.read_frame_file( + frame_file, start_time=start_time, end_time=start_time + duration, + buffer_time=buffer_time, channel=channel, + resample=sampling_frequency) + + self.set_from_gwpy_timeseries(strain) + + def _set_time_and_frequency_array_parameters(self, duration, sampling_frequency, start_time): + self._times_and_frequencies = CoupledTimeAndFrequencySeries(duration=duration, + sampling_frequency=sampling_frequency, + start_time=start_time) + + @property + def sampling_frequency(self): + return self._times_and_frequencies.sampling_frequency + + @sampling_frequency.setter + def sampling_frequency(self, sampling_frequency): + self._times_and_frequencies.sampling_frequency = sampling_frequency + + @property + def duration(self): + return self._times_and_frequencies.duration + + @duration.setter + def duration(self, duration): + self._times_and_frequencies.duration = duration + + @property + def start_time(self): + return self._times_and_frequencies.start_time + + @start_time.setter + def start_time(self, start_time): + self._times_and_frequencies.start_time = start_time + + @property + def frequency_array(self): + """ Frequencies of the data in Hz """ + return self._times_and_frequencies.frequency_array + + @frequency_array.setter + def frequency_array(self, frequency_array): + self._times_and_frequencies.frequency_array = frequency_array + + @property + def time_array(self): + """ Time of the data in seconds """ + return self._times_and_frequencies.time_array + + @time_array.setter + def time_array(self, time_array): + self._times_and_frequencies.time_array = time_array diff --git a/setup.py b/setup.py index f571a0569bbd77f92b73975f9e43594af948ac36..f1b0dac5a287c7570892e2c1e132d60666556aa2 100644 --- a/setup.py +++ b/setup.py @@ -70,10 +70,11 @@ setup(name='bilby', license="MIT", version=VERSION, packages=['bilby', 'bilby.core', 'bilby.core.sampler', - 'bilby.gw', 'bilby.hyper', 'cli_bilby'], + 'bilby.gw', 'bilby.gw.detector', 'bilby.gw.sampler', + 'bilby.hyper', 'cli_bilby'], package_dir={'bilby': 'bilby'}, - package_data={'bilby.gw': ['prior_files/*', 'noise_curves/*.txt', - 'detectors/*'], + package_data={'bilby.gw': ['prior_files/*'], + 'bilby.gw.detector': ['noise_curves/*.txt', 'detectors/*'], 'bilby': [version_file]}, install_requires=[ 'future', diff --git a/test/detector_test.py b/test/detector_test.py index abcf5a17a53d242f14ddf95379737bd2367c9b52..e3f83434b3dc8b797d16cbf8b51e121029ec31bc 100644 --- a/test/detector_test.py +++ b/test/detector_test.py @@ -845,7 +845,7 @@ class TestInterferometerList(unittest.TestCase): def test_init_with_string_list(self): """ Merely checks if this ends up in the right bracket """ - with mock.patch('bilby.gw.detector.get_empty_interferometer') as m: + with mock.patch('bilby.gw.detector.networks.get_empty_interferometer') as m: m.side_effect = TypeError with self.assertRaises(TypeError): bilby.gw.detector.InterferometerList(['string'])