def td_output_vector(freqs, damping_times, taper=False, delta_t=None, t_final=None): """Return an empty TimeSeries with the appropriate size to fit all the quasi-normal modes present in freqs, damping_times """ if not delta_t: delta_t = lm_deltat(freqs, damping_times) if not t_final: t_final = lm_tfinal(damping_times) kmax = int(t_final / delta_t) + 1 # Different modes will have different tapering window-size # Find maximum window size to create long enough output vector if taper: max_tau = max(damping_times.values()) if \ isinstance(damping_times, dict) else damping_times kmax += int(max_tau/delta_t) outplus = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) outcross = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) if taper: # Change epoch of output vector if tapering will be applied start = - max_tau # To ensure that t=0 is still in output vector start -= start % delta_t outplus._epoch, outcross._epoch = start, start return outplus, outcross
def get_td_qnm(template=None, taper=None, **kwargs): """Return a time domain damped sinusoid. Parameters ---------- template: object An object that has attached properties. This can be used to substitute for keyword arguments. A common example would be a row in an xml table. taper: {None, float}, optional Tapering at the beginning of the waveform with duration taper * tau. This option is recommended with timescales taper=1./2 or 1. for time-domain ringdown-only injections. The abrupt turn on of the ringdown can cause issues on the waveform when doing the fourier transform to the frequency domain. Setting taper will add a rapid ringup with timescale tau/10. f_0 : float The ringdown-frequency. tau : float The damping time of the sinusoid. phi : float The initial phase of the ringdown. amp : float The amplitude of the ringdown (constant for now). delta_t : {None, float}, optional The time step used to generate the ringdown. If None, it will be set to the inverse of the frequency at which the amplitude is 1/1000 of the peak amplitude. t_final : {None, float}, optional The ending time of the output time series. If None, it will be set to the time at which the amplitude is 1/1000 of the peak amplitude. Returns ------- hplus: TimeSeries The plus phase of the ringdown in time domain. hcross: TimeSeries The cross phase of the ringdown in time domain. """ input_params = props(template, qnm_required_args, **kwargs) f_0 = input_params.pop('f_0') tau = input_params.pop('tau') amp = input_params.pop('amp') phi = input_params.pop('phi') # the following may not be in input_params delta_t = input_params.pop('delta_t', None) t_final = input_params.pop('t_final', None) if delta_t is None: delta_t = 1. / qnm_freq_decay(f_0, tau, 1. / 1000) if delta_t < min_dt: delta_t = min_dt if t_final is None: t_final = qnm_time_decay(tau, 1. / 1000) kmax = int(t_final / delta_t) + 1 times = numpy.arange(kmax) * delta_t hp = amp * numpy.exp(-times / tau) * numpy.cos(two_pi * f_0 * times + phi) hc = amp * numpy.exp(-times / tau) * numpy.sin(two_pi * f_0 * times + phi) # If size of tapering window is less than delta_t, do not apply taper. if taper is None or delta_t > taper * tau: hplus = TimeSeries(zeros(kmax), delta_t=delta_t) hcross = TimeSeries(zeros(kmax), delta_t=delta_t) hplus.data[:kmax] = hp hcross.data[:kmax] = hc return hplus, hcross else: taper_hp, taper_hc, taper_window, start = apply_taper( delta_t, taper, f_0, tau, amp, phi) hplus = TimeSeries(zeros(taper_window + kmax), delta_t=delta_t) hcross = TimeSeries(zeros(taper_window + kmax), delta_t=delta_t) hplus.data[:taper_window] = taper_hp hplus.data[taper_window:] = hp hplus._epoch = start hcross.data[:taper_window] = taper_hc hcross.data[taper_window:] = hc hcross._epoch = start return hplus, hcross
def get_td_from_freqtau(template=None, taper=None, **kwargs): """Return time domain ringdown with all the modes specified. Parameters ---------- template: object An object that has attached properties. This can be used to substitute for keyword arguments. A common example would be a row in an xml table. taper: {None, float}, optional Tapering at the beginning of the waveform with duration taper * tau. This option is recommended with timescales taper=1./2 or 1. for time-domain ringdown-only injections. The abrupt turn on of the ringdown can cause issues on the waveform when doing the fourier transform to the frequency domain. Setting taper will add a rapid ringup with timescale tau/10. Each mode and overtone will have a different taper depending on its tau, the final taper being the superposition of all the tapers. lmns : list Desired lmn modes as strings (lm modes available: 22, 21, 33, 44, 55). The n specifies the number of overtones desired for the corresponding lm pair (maximum n=8). Example: lmns = ['223','331'] are the modes 220, 221, 222, and 330 f_lmn: float Central frequency of the lmn overtone, as many as number of modes. tau_lmn: float Damping time of the lmn overtone, as many as number of modes. amp220 : float Amplitude of the fundamental 220 mode. amplmn : float Fraction of the amplitude of the lmn overtone relative to the fundamental mode, as many as the number of subdominant modes. philmn : float Phase of the lmn overtone, as many as the number of modes. Should also include the information from the azimuthal angle (phi + m*Phi). inclination : {None, float}, optional Inclination of the system in radians. If None, the spherical harmonics will be set to 1. delta_t : {None, float}, optional The time step used to generate the ringdown. If None, it will be set to the inverse of the frequency at which the amplitude is 1/1000 of the peak amplitude (the minimum of all modes). t_final : {None, float}, optional The ending time of the output frequency series. If None, it will be set to the time at which the amplitude is 1/1000 of the peak amplitude (the maximum of all modes). Returns ------- hplustilde: FrequencySeries The plus phase of a ringdown with the lm modes specified and n overtones in frequency domain. hcrosstilde: FrequencySeries The cross phase of a ringdown with the lm modes specified and n overtones in frequency domain. """ input_params = props(template, freqtau_required_args, **kwargs) # Get required args f_0, tau = lm_freqs_taus(**input_params) lmns = input_params['lmns'] for lmn in lmns: if int(lmn[2]) == 0: raise ValueError('Number of overtones (nmodes) must be greater ' 'than zero.') # following may not be in input_params inc = input_params.pop('inclination', None) delta_t = input_params.pop('delta_t', None) t_final = input_params.pop('t_final', None) if not delta_t: delta_t = lm_deltat(f_0, tau, lmns) if not t_final: t_final = lm_tfinal(tau, lmns) kmax = int(t_final / delta_t) + 1 # Different overtones will have different tapering window-size # Find maximum window size to create long enough output vector if taper: taper_window = int(taper*max(tau.values())/delta_t) kmax += taper_window outplus = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) outcross = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) if taper: start = - taper * max(tau.values()) outplus._epoch, outcross._epoch = start, start for lmn in lmns: l, m, nmodes = int(lmn[0]), int(lmn[1]), int(lmn[2]) hplus, hcross = get_td_lm(freqs=f_0, taus=tau, l=l, m=m, nmodes=nmodes, taper=taper, inclination=inc, delta_t=delta_t, t_final=t_final, **input_params) if not taper: outplus.data += hplus.data outcross.data += hcross.data else: outplus = taper_shift(hplus, outplus) outcross = taper_shift(hcross, outcross) return outplus, outcross
def get_td_lm(template=None, taper=None, **kwargs): """Return time domain lm mode with the given number of overtones. Parameters ---------- template: object An object that has attached properties. This can be used to substitute for keyword arguments. A common example would be a row in an xml table. taper: {None, float}, optional Tapering at the beginning of the waveform with duration taper * tau. This option is recommended with timescales taper=1./2 or 1. for time-domain ringdown-only injections. The abrupt turn on of the ringdown can cause issues on the waveform when doing the fourier transform to the frequency domain. Setting taper will add a rapid ringup with timescale tau/10. Each overtone will have a different taper depending on its tau, the final taper being the superposition of all the tapers. freqs : dict {lmn:f_lmn} Dictionary of the central frequencies for each overtone, as many as number of modes. taus : dict {lmn:tau_lmn} Dictionary of the damping times for each overtone, as many as number of modes. l : int l mode (lm modes available: 22, 21, 33, 44, 55). m : int m mode (lm modes available: 22, 21, 33, 44, 55). nmodes: int Number of overtones desired (maximum n=8) amp220 : float Amplitude of the fundamental 220 mode, needed for any lm. amplmn : float Fraction of the amplitude of the lmn overtone relative to the fundamental mode, as many as the number of subdominant modes. philmn : float Phase of the lmn overtone, as many as the number of modes. Should also include the information from the azimuthal angle (phi + m*Phi). inclination : {None, float}, optional Inclination of the system in radians for the spherical harmonics. delta_t : {None, float}, optional The time step used to generate the ringdown. If None, it will be set to the inverse of the frequency at which the amplitude is 1/1000 of the peak amplitude (the minimum of all modes). t_final : {None, float}, optional The ending time of the output time series. If None, it will be set to the time at which the amplitude is 1/1000 of the peak amplitude (the maximum of all modes). Returns ------- hplus: TimeSeries The plus phase of a lm mode with overtones (n) in time domain. hcross: TimeSeries The cross phase of a lm mode with overtones (n) in time domain. """ input_params = props(template, lm_required_args, **kwargs) # Get required args amps, phis = lm_amps_phases(**input_params) f_0 = input_params.pop('freqs') tau = input_params.pop('taus') inc = input_params.pop('inclination', None) l, m = input_params.pop('l'), input_params.pop('m') nmodes = input_params.pop('nmodes') if int(nmodes) == 0: raise ValueError('Number of overtones (nmodes) must be greater ' 'than zero.') # The following may not be in input_params delta_t = input_params.pop('delta_t', None) t_final = input_params.pop('t_final', None) if not delta_t: delta_t = lm_deltat(f_0, tau, ['%d%d%d' %(l,m,nmodes)]) if not t_final: t_final = lm_tfinal(tau, ['%d%d%d' %(l, m, nmodes)]) kmax = int(t_final / delta_t) + 1 # Different overtones will have different tapering window-size # Find maximum window size to create long enough output vector if taper: taper_window = int(taper*max(tau.values())/delta_t) kmax += taper_window outplus = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) outcross = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) if taper: start = - taper * max(tau.values()) outplus._epoch, outcross._epoch = start, start for n in range(nmodes): hplus, hcross = get_td_qnm(template=None, taper=taper, f_0=f_0['%d%d%d' %(l,m,n)], tau=tau['%d%d%d' %(l,m,n)], phi=phis['%d%d%d' %(l,m,n)], amp=amps['%d%d%d' %(l,m,n)], inclination=inc, l=l, m=m, delta_t=delta_t, t_final=t_final) if not taper: outplus.data += hplus.data outcross.data += hcross.data else: outplus = taper_shift(hplus, outplus) outcross = taper_shift(hcross, outcross) return outplus, outcross
def get_td_qnm(template=None, taper=None, **kwargs): """Return a time domain damped sinusoid. Parameters ---------- template: object An object that has attached properties. This can be used to substitute for keyword arguments. A common example would be a row in an xml table. taper: {None, float}, optional Tapering at the beginning of the waveform with duration taper * tau. This option is recommended with timescales taper=1./2 or 1. for time-domain ringdown-only injections. The abrupt turn on of the ringdown can cause issues on the waveform when doing the fourier transform to the frequency domain. Setting taper will add a rapid ringup with timescale tau/10. f_0 : float The ringdown-frequency. tau : float The damping time of the sinusoid. amp : float The amplitude of the ringdown (constant for now). phi : float The initial phase of the ringdown. Should also include the information from the azimuthal angle (phi_0 + m*Phi) inclination : {None, float}, optional Inclination of the system in radians for the spherical harmonics. l : {2, int}, optional l mode for the spherical harmonics. Default is l=2. m : {2, int}, optional m mode for the spherical harmonics. Default is m=2. delta_t : {None, float}, optional The time step used to generate the ringdown. If None, it will be set to the inverse of the frequency at which the amplitude is 1/1000 of the peak amplitude. t_final : {None, float}, optional The ending time of the output time series. If None, it will be set to the time at which the amplitude is 1/1000 of the peak amplitude. Returns ------- hplus: TimeSeries The plus phase of the ringdown in time domain. hcross: TimeSeries The cross phase of the ringdown in time domain. """ input_params = props(template, qnm_required_args, **kwargs) f_0 = input_params.pop('f_0') tau = input_params.pop('tau') amp = input_params.pop('amp') phi = input_params.pop('phi') # the following may not be in input_params inc = input_params.pop('inclination', None) l = input_params.pop('l', 2) m = input_params.pop('m', 2) delta_t = input_params.pop('delta_t', None) t_final = input_params.pop('t_final', None) if not delta_t: delta_t = 1. / qnm_freq_decay(f_0, tau, 1./1000) if delta_t < min_dt: delta_t = min_dt if not t_final: t_final = qnm_time_decay(tau, 1./1000) kmax = int(t_final / delta_t) + 1 times = numpy.arange(kmax) * delta_t if inc is not None: Y_plus, Y_cross = spher_harms(l, m, inc) else: Y_plus, Y_cross = 1, 1 hplus = amp * Y_plus * numpy.exp(-times/tau) * \ numpy.cos(two_pi*f_0*times + phi) hcross = amp * Y_cross * numpy.exp(-times/tau) * \ numpy.sin(two_pi*f_0*times + phi) if taper and delta_t < taper*tau: taper_window = int(taper*tau/delta_t) kmax += taper_window outplus = TimeSeries(zeros(kmax), delta_t=delta_t) outcross = TimeSeries(zeros(kmax), delta_t=delta_t) # If size of tapering window is less than delta_t, do not apply taper. if not taper or delta_t > taper*tau: outplus.data[:kmax] = hplus outcross.data[:kmax] = hcross return outplus, outcross else: taper_hp, taper_hc = apply_taper(delta_t, taper, f_0, tau, amp, phi, l, m, inc) start = - taper * tau outplus.data[:taper_window] = taper_hp outplus.data[taper_window:] = hplus outcross.data[:taper_window] = taper_hc outcross.data[taper_window:] = hcross outplus._epoch, outcross._epoch = start, start return outplus, outcross
def get_td_from_freqtau(template=None, taper=None, **kwargs): """Return time domain ringdown with all the modes specified. Parameters ---------- template: object An object that has attached properties. This can be used to substitute for keyword arguments. A common example would be a row in an xml table. taper: {None, float}, optional Tapering at the beginning of the waveform with duration taper * tau. This option is recommended with timescales taper=1./2 or 1. for time-domain ringdown-only injections. The abrupt turn on of the ringdown can cause issues on the waveform when doing the fourier transform to the frequency domain. Setting taper will add a rapid ringup with timescale tau/10. Each mode and overtone will have a different taper depending on its tau, the final taper being the superposition of all the tapers. lmns : list Desired lmn modes as strings (lm modes available: 22, 21, 33, 44, 55). The n specifies the number of overtones desired for the corresponding lm pair (maximum n=8). Example: lmns = ['223','331'] are the modes 220, 221, 222, and 330 f_lmn: float Central frequency of the lmn overtone, as many as number of modes. tau_lmn: float Damping time of the lmn overtone, as many as number of modes. amp220 : float Amplitude of the fundamental 220 mode. amplmn : float Fraction of the amplitude of the lmn overtone relative to the fundamental mode, as many as the number of subdominant modes. philmn : float Phase of the lmn overtone, as many as the number of modes. Should also include the information from the azimuthal angle (phi + m*Phi). inclination : {0., float}, optional Inclination of the system in radians. Default is 0 (face on). delta_t : {None, float}, optional The time step used to generate the ringdown. If None, it will be set to the inverse of the frequency at which the amplitude is 1/1000 of the peak amplitude (the minimum of all modes). t_final : {None, float}, optional The ending time of the output frequency series. If None, it will be set to the time at which the amplitude is 1/1000 of the peak amplitude (the maximum of all modes). Returns ------- hplustilde: FrequencySeries The plus phase of a ringdown with the lm modes specified and n overtones in frequency domain. hcrosstilde: FrequencySeries The cross phase of a ringdown with the lm modes specified and n overtones in frequency domain. """ input_params = props(template, freqtau_required_args, **kwargs) # Get required args f_0, tau = lm_freqs_taus(**input_params) lmns = input_params['lmns'] for lmn in lmns: if int(lmn[2]) == 0: raise ValueError('Number of overtones (nmodes) must be greater ' 'than zero.') # following may not be in input_params inc = input_params.pop('inclination', 0.) delta_t = input_params.pop('delta_t', None) t_final = input_params.pop('t_final', None) if delta_t is None: delta_t = lm_deltat(f_0, tau, lmns) if t_final is None: t_final = lm_tfinal(tau, lmns) kmax = int(t_final / delta_t) + 1 # Different overtones will have different tapering window-size # Find maximum window size to create long enough output vector if taper is not None: taper_window = int(taper*max(tau.values())/delta_t) kmax += taper_window outplus = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) outcross = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) if taper is not None: start = - taper * max(tau.values()) outplus._epoch, outcross._epoch = start, start for lmn in lmns: l, m, nmodes = int(lmn[0]), int(lmn[1]), int(lmn[2]) hplus, hcross = get_td_lm(freqs=f_0, taus=tau, l=l, m=m, nmodes=nmodes, taper=taper, inclination=inc, delta_t=delta_t, t_final=t_final, **input_params) if taper is None: outplus.data += hplus.data outcross.data += hcross.data else: outplus = taper_shift(hplus, outplus) outcross = taper_shift(hcross, outcross) return outplus, outcross
def get_td_lm(template=None, taper=None, **kwargs): """Return time domain lm mode with the given number of overtones. Parameters ---------- template: object An object that has attached properties. This can be used to substitute for keyword arguments. A common example would be a row in an xml table. taper: {None, float}, optional Tapering at the beginning of the waveform with duration taper * tau. This option is recommended with timescales taper=1./2 or 1. for time-domain ringdown-only injections. The abrupt turn on of the ringdown can cause issues on the waveform when doing the fourier transform to the frequency domain. Setting taper will add a rapid ringup with timescale tau/10. Each overtone will have a different taper depending on its tau, the final taper being the superposition of all the tapers. freqs : dict {lmn:f_lmn} Dictionary of the central frequencies for each overtone, as many as number of modes. taus : dict {lmn:tau_lmn} Dictionary of the damping times for each overtone, as many as number of modes. l : int l mode (lm modes available: 22, 21, 33, 44, 55). m : int m mode (lm modes available: 22, 21, 33, 44, 55). nmodes: int Number of overtones desired (maximum n=8) amp220 : float Amplitude of the fundamental 220 mode, needed for any lm. amplmn : float Fraction of the amplitude of the lmn overtone relative to the fundamental mode, as many as the number of subdominant modes. philmn : float Phase of the lmn overtone, as many as the number of modes. Should also include the information from the azimuthal angle (phi + m*Phi). inclination : {0., float}, optional Inclination of the system in radians. Default is 0 (face on). delta_t : {None, float}, optional The time step used to generate the ringdown. If None, it will be set to the inverse of the frequency at which the amplitude is 1/1000 of the peak amplitude (the minimum of all modes). t_final : {None, float}, optional The ending time of the output time series. If None, it will be set to the time at which the amplitude is 1/1000 of the peak amplitude (the maximum of all modes). Returns ------- hplus: TimeSeries The plus phase of a lm mode with overtones (n) in time domain. hcross: TimeSeries The cross phase of a lm mode with overtones (n) in time domain. """ input_params = props(template, lm_required_args, **kwargs) # Get required args amps, phis = lm_amps_phases(**input_params) f_0 = input_params.pop('freqs') tau = input_params.pop('taus') inc = input_params.pop('inclination', 0.) l, m = input_params.pop('l'), input_params.pop('m') nmodes = input_params.pop('nmodes') if int(nmodes) == 0: raise ValueError('Number of overtones (nmodes) must be greater ' 'than zero.') # The following may not be in input_params delta_t = input_params.pop('delta_t', None) t_final = input_params.pop('t_final', None) if delta_t is None: delta_t = lm_deltat(f_0, tau, ['%d%d%d' %(l,m,nmodes)]) if t_final is None: t_final = lm_tfinal(tau, ['%d%d%d' %(l, m, nmodes)]) kmax = int(t_final / delta_t) + 1 # Different overtones will have different tapering window-size # Find maximum window size to create long enough output vector if taper is not None: taper_window = int(taper*max(tau.values())/delta_t) kmax += taper_window outplus = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) outcross = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t) if taper is not None: start = - taper * max(tau.values()) outplus._epoch, outcross._epoch = start, start for n in range(nmodes): hplus, hcross = get_td_qnm(template=None, taper=taper, f_0=f_0['%d%d%d' %(l,m,n)], tau=tau['%d%d%d' %(l,m,n)], phi=phis['%d%d%d' %(l,m,n)], amp=amps['%d%d%d' %(l,m,n)], inclination=inc, l=l, m=m, delta_t=delta_t, t_final=t_final) if taper is None: outplus.data += hplus.data outcross.data += hcross.data else: outplus = taper_shift(hplus, outplus) outcross = taper_shift(hcross, outcross) return outplus, outcross
def get_td_qnm(template=None, taper=None, **kwargs): """Return a time domain damped sinusoid. Parameters ---------- template: object An object that has attached properties. This can be used to substitute for keyword arguments. A common example would be a row in an xml table. taper: {None, float}, optional Tapering at the beginning of the waveform with duration taper * tau. This option is recommended with timescales taper=1./2 or 1. for time-domain ringdown-only injections. The abrupt turn on of the ringdown can cause issues on the waveform when doing the fourier transform to the frequency domain. Setting taper will add a rapid ringup with timescale tau/10. f_0 : float The ringdown-frequency. tau : float The damping time of the sinusoid. amp : float The amplitude of the ringdown (constant for now). phi : float The initial phase of the ringdown. Should also include the information from the azimuthal angle (phi_0 + m*Phi) inclination : {0., float}, optional Inclination of the system in radians. Default is 0 (face on). l : {2, int}, optional l mode for the spherical harmonics. Default is l=2. m : {2, int}, optional m mode for the spherical harmonics. Default is m=2. delta_t : {None, float}, optional The time step used to generate the ringdown. If None, it will be set to the inverse of the frequency at which the amplitude is 1/1000 of the peak amplitude. t_final : {None, float}, optional The ending time of the output time series. If None, it will be set to the time at which the amplitude is 1/1000 of the peak amplitude. Returns ------- hplus: TimeSeries The plus phase of the ringdown in time domain. hcross: TimeSeries The cross phase of the ringdown in time domain. """ input_params = props(template, qnm_required_args, **kwargs) f_0 = input_params.pop('f_0') tau = input_params.pop('tau') amp = input_params.pop('amp') phi = input_params.pop('phi') # the following may not be in input_params inc = input_params.pop('inclination', 0.) l = input_params.pop('l', 2) m = input_params.pop('m', 2) delta_t = input_params.pop('delta_t', None) t_final = input_params.pop('t_final', None) if delta_t is None: delta_t = 1. / qnm_freq_decay(f_0, tau, 1./1000) if delta_t < min_dt: delta_t = min_dt if t_final is None: t_final = qnm_time_decay(tau, 1./1000) kmax = int(t_final / delta_t) + 1 times = numpy.arange(kmax) * delta_t Y_plus, Y_cross = spher_harms(l, m, inc) hplus = amp * Y_plus * numpy.exp(-times/tau) * \ numpy.cos(two_pi*f_0*times + phi) hcross = amp * Y_cross * numpy.exp(-times/tau) * \ numpy.sin(two_pi*f_0*times + phi) if taper is not None and delta_t < taper*tau: taper_window = int(taper*tau/delta_t) kmax += taper_window outplus = TimeSeries(zeros(kmax), delta_t=delta_t) outcross = TimeSeries(zeros(kmax), delta_t=delta_t) # If size of tapering window is less than delta_t, do not apply taper. if taper is None or delta_t > taper*tau: outplus.data[:kmax] = hplus outcross.data[:kmax] = hcross return outplus, outcross else: taper_hp, taper_hc = apply_taper(delta_t, taper, f_0, tau, amp, phi, l, m, inc) start = - taper * tau outplus.data[:taper_window] = taper_hp outplus.data[taper_window:] = hplus outcross.data[:taper_window] = taper_hc outcross.data[taper_window:] = hcross outplus._epoch, outcross._epoch = start, start return outplus, outcross
def align_waveforms_amplitude_peak(hplus1, hcross1, hplus2, hcross2, shift_epochs_only=True, trim_leading=False, trim_trailing=True, verbose=False): """ Align the two waveforms, shifting only one of the two. - AT the Amplitude PEAK """ _dt = 1.0 if type(hplus1) == TimeSeries: _dt = hplus1.delta_t elif type(hplus2) == TimeSeries: _dt = hplus2.delta_t if type(hcross1) != TimeSeries: _dt = hcross1.delta_t if type(hcross2) != TimeSeries: _dt = hcross2.delta_t hp1 = TimeSeries(hplus1, delta_t=_dt, copy=True) hc1 = TimeSeries(hcross1, delta_t=_dt, copy=True) hp2 = TimeSeries(hplus2, delta_t=_dt, copy=True) hc2 = TimeSeries(hcross2, delta_t=_dt, copy=True) # Get amplitude peak for 1st set of polarizations amp1 = amplitude_from_polarizations(hp1, hc1) amp1I = InterpolatedUnivariateSpline(amp1.sample_times, -1 * amp1.data) x0 = np.float64( amp1.sample_times[np.where(amp1.data == max(amp1.data))[0][0]]) tmp = minimize_scalar(amp1I, x0, method='bounded', bounds=(x0 - 10 * amp1.delta_t, x0 + 10 * amp1.delta_t)) h1_max_amp_time = tmp['x'] h1_max_amp = -1 * tmp['fun'] # Get amplitude peak for 1st set of polarizations amp2 = amplitude_from_polarizations(hp2, hc2) amp2I = InterpolatedUnivariateSpline(amp2.sample_times, -1 * amp2.data) x0 = np.float64( amp2.sample_times[(np.where(amp2.data == max(amp2.data))[0][0])]) tmp = minimize_scalar(amp2I, x0, method='bounded', bounds=(x0 - 10 * amp2.delta_t, x0 + 10 * amp2.delta_t)) h2_max_amp_time = tmp['x'] h2_max_amp = -1 * tmp['fun'] if verbose: print(("h1 max time = %f, epoch = %f" % (h1_max_amp_time, float(hp1._epoch)))) print(("h2 max time = %f, epoch = %f" % (h2_max_amp_time, float(hp2._epoch)))) # Amplitude location from the start t1 = h1_max_amp_time t2 = h2_max_amp_time t_shift = t1 - t2 if verbose: print(("time shift = %f to be added to waveform 2" % t_shift)) # Find phase shift phs1 = phase_from_polarizations(hp1, hc1) phs2 = phase_from_polarizations(hp2, hc2) phs1I = InterpolatedUnivariateSpline(phs1.sample_times, phs1.data) phs2I = InterpolatedUnivariateSpline(phs2.sample_times, phs2.data) ph1 = phs1I(h1_max_amp_time) ph2 = phs2I(h2_max_amp_time) ph_shift = np.float64(ph1 - ph2) if verbose: print(("phase1 at peak idx = %d, = %f" % (int(np.round(t1 / hp1.delta_t)), ph1))) print(("phase2 at peak idx = %d, = %f" % (int(np.round(t2 / hp2.delta_t)), ph2))) print(("phase shift = %f, time shift = %f" % (ph_shift, t_shift))) # # Shift whichever needs to be shifted to future time. # Shifting back in time is tricky. if shift_epochs_only: hp2, hc2 = shift_waveform_phase_time(hp2, hc2, t_shift, ph_shift, shift_epochs_only=True, verbose=verbose) # Finally, shift everything's peak to t=0 hp1._epoch -= h1_max_amp_time hc1._epoch -= h1_max_amp_time hp2._epoch -= h1_max_amp_time hc2._epoch -= h1_max_amp_time # Trim leading zeros. If time shifts are actual shifts of data along the # array, the leading zeros have meaning and cannot be trimmed. if trim_leading and shift_epochs_only: hp1 = trim_leading_zeros(hp1) hc1 = trim_leading_zeros(hc1) hp2 = trim_leading_zeros(hp2) hc2 = trim_leading_zeros(hc2) else: t_shift += (amp2.sample_times[0] - amp1.sample_times[0]) if verbose: print("phase shift is actually = {}, time shift = {}".format( ph_shift, t_shift)) if t_shift >= 0: hp2, hc2 = shift_waveform_phase_time(hp2, hc2, t_shift, ph_shift, shift_epochs_only=False, verbose=verbose) hp1._epoch -= h1_max_amp_time hc1._epoch -= h1_max_amp_time hp2._epoch = hp1._epoch hc2._epoch = hc1._epoch else: hp1, hc1 = shift_waveform_phase_time(hp1, hc1, -1 * t_shift, ph_shift, shift_epochs_only=False, verbose=verbose) hp2._epoch -= h2_max_amp_time hc2._epoch -= h2_max_amp_time hp1._epoch = hp2._epoch hc1._epoch = hc2._epoch # Trim any trailing zeros if trim_trailing: hp1 = trim_trailing_zeros(hp1) hc1 = trim_trailing_zeros(hc1) hp2 = trim_trailing_zeros(hp2) hc2 = trim_trailing_zeros(hc2) return hp1, hc1, hp2, hc2
def get_td_qnm(template=None, taper=None, **kwargs): """Return a time domain damped sinusoid. Parameters ---------- template: object An object that has attached properties. This can be used to substitute for keyword arguments. A common example would be a row in an xml table. taper: {None, float}, optional Tapering at the beginning of the waveform with duration taper * tau. This option is recommended with timescales taper=1./2 or 1. for time-domain ringdown-only injections. The abrupt turn on of the ringdown can cause issues on the waveform when doing the fourier transform to the frequency domain. Setting taper will add a rapid ringup with timescale tau/10. f_0 : float The ringdown-frequency. tau : float The damping time of the sinusoid. phi : float The initial phase of the ringdown. amp : float The amplitude of the ringdown (constant for now). delta_t : {None, float}, optional The time step used to generate the ringdown. If None, it will be set to the inverse of the frequency at which the amplitude is 1/1000 of the peak amplitude. t_final : {None, float}, optional The ending time of the output time series. If None, it will be set to the time at which the amplitude is 1/1000 of the peak amplitude. Returns ------- hplus: TimeSeries The plus phase of the ringdown in time domain. hcross: TimeSeries The cross phase of the ringdown in time domain. """ input_params = props(template, qnm_required_args, **kwargs) f_0 = input_params.pop('f_0') tau = input_params.pop('tau') amp = input_params.pop('amp') phi = input_params.pop('phi') # the following may not be in input_params delta_t = input_params.pop('delta_t', None) t_final = input_params.pop('t_final', None) if delta_t is None: delta_t = 1. / qnm_freq_decay(f_0, tau, 1./1000) if delta_t < min_dt: delta_t = min_dt if t_final is None: t_final = qnm_time_decay(tau, 1./1000) kmax = int(t_final / delta_t) + 1 times = numpy.arange(kmax) * delta_t hp = amp * numpy.exp(-times/tau) * numpy.cos(two_pi*f_0*times + phi) hc = amp * numpy.exp(-times/tau) * numpy.sin(two_pi*f_0*times + phi) # If size of tapering window is less than delta_t, do not apply taper. if taper is None or delta_t > taper*tau: hplus = TimeSeries(zeros(kmax), delta_t=delta_t) hcross = TimeSeries(zeros(kmax), delta_t=delta_t) hplus.data[:kmax] = hp hcross.data[:kmax] = hc return hplus, hcross else: taper_hp, taper_hc, taper_window, start = apply_taper(delta_t, taper, f_0, tau, amp, phi) hplus = TimeSeries(zeros(taper_window+kmax), delta_t=delta_t) hcross = TimeSeries(zeros(taper_window+kmax), delta_t=delta_t) hplus.data[:taper_window] = taper_hp hplus.data[taper_window:] = hp hplus._epoch = start hcross.data[:taper_window] = taper_hc hcross.data[taper_window:] = hc hcross._epoch = start return hplus, hcross