Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
gw.py
Go to the documentation of this file.
1from astropy.coordinates import Angle, SkyCoord
2from astropy.time import Time
3from astropy import units as u
4from typing import Dict, NamedTuple, Union
5from gwpy.timeseries import TimeSeries
6from gwpy.frequencyseries import FrequencySeries
7import lal
8import lalsimulation as lalsim
9import numpy as np
10import warnings
11
13 hp: Union[TimeSeries, FrequencySeries]
14 hc: Union[TimeSeries, FrequencySeries]
15 """
16 GravitationalWavePolarizations class that takes hp,hc GwPy Time/FrequencySeries objects as inputs. Provides method to compute
17 zero noise strain given a detector, sky-position, polarization and time of arrival values.
18 Parameters
19 ----------
20 hp, hc : GwPy `TimeSeries` or `FrequencySeries` objects
21 """
22 def domain(self):
23 if isinstance(self.hp, TimeSeries) and isinstance(self.hc, TimeSeries):
24 return 'time'
25 elif isinstance(self.hp, FrequencySeries) and isinstance(self.hc, FrequencySeries):
26 return 'frequency'
27 else:
28 return 'mixed'
29
30 def strain(self, det, ra, dec, psi, tgps):
31 """
32 Compute the detector strain in zero-noise
33
34 Parameters
35 ----------
36 det : `str`
37 Name of the detector, for eg: 'H1' or 'L1'
38 ra : `~astropy.units.Quantity`
39 Right-ascension of the binary in units of rad
40 dec : `~astropy.units.Quantity`
41 Declination of the binary in units of rad
42 psi : `~astropy.units.Quantity`
43 Polarization in units of rad
44 tgps : `float`, `~astropy.units.Quantity`
45 GPS Time of time-of-arrival of signal. Either as float or in units of s
46
47 Returns
48 -------
49 h : `TimeSeries` or `FrequencySeries` object
50 Waveform recomposed with detector response (Fp*hp + Fc*hc)
51 """
52
53 # Might change it later; for now keeping it as used by ligo.
54 warnings.warn("This code is currently UNREVIEWED, use with caution!!")
55 pos = SkyCoord(ra = ra, dec = dec)
56 psi = Angle(psi)
57 t = Time(tgps, format='gps', scale='utc')
58
59 if isinstance(det, str):
60 det = lalsim.DetectorPrefixToLALDetector(det)
61
62 if self.domain() == 'time':
63 hp = self.hp.to_lal()
64 hc = self.hc.to_lal()
65 hp.epoch += t.gps
66 hc.epoch += t.gps
67 h = lalsim.SimDetectorStrainREAL8TimeSeries(hp, hc, pos.ra.rad, pos.dec.rad, psi.rad, det)
68 h = TimeSeries.from_lal(h)
69 return h
70
71 elif self.domain() == 'frequency':
72 # WARNING: does not account for Earth rotation or non-LWL effects
73
74 epoch = lal.LIGOTimeGPS(t.gps)
75 dt = lal.TimeDelayFromEarthCenter(det.location, pos.ra.rad, pos.dec.rad, epoch)
76 fp, fc = lal.ComputeDetAMResponse(det.response, pos.ra.rad, pos.dec.rad, psi.rad, lal.GreenwichMeanSiderealTime(epoch))
77
78 # Shouldn't this have a overall time shift due to time-delay from earth center? or does epoch take care of it?
79 # TODO: adress during phase 2 of review
80
81 h = (fp * self.hp + fc * self.hc)
82 h.epoch = Time(t.gps + dt, format='gps', scale='utc')
83 return h
84 else:
85 return ValueError('hp and hc must both be either TimeSeries or FrequencySeries')
86
87 def add_strain(self, data, det, ra, dec, psi, tgps, response=None):
88 """
89 Add strain to some LAL strain as required
90
91 Parameters:
92 data : LAL TimeSeries data
93 Data in which to add detector response strain
94 det : `str`
95 Name of the detector, for eg: 'H1' or 'L1'
96 ra : `~astropy.units.Quantity`
97 Right-ascension of the binary in units of rad
98 dec : `~astropy.units.Quantity`
99 Declination of the binary in units of rad
100 psi : `~astropy.units.Quantity`
101 Polarization in units of rad
102 tgps : `float`, `~astropy.units.Quantity`
103 GPS Time of time-of-arrival of signal. Either as float or in units of s
104 response : `COMPLEX16FrequencySeries`
105 Response function passed to lalsimulation.SimAddInjectionREAL8TimeSeries,
106 transforming strain to detector output units. Use None for unit response.
107
108 Returns
109 -------
110 data : `TimeSeries` object
111 Detector response injected in strain data
112 """
113 warnings.warn("This code is currently UNREVIEWED, use with caution!")
114 h = self.strain(det, ra, dec, psi, tgps)
115 # Adding condition to convert FrequencySeries to TimeSeries if strain is in freq domain
116 if isinstance(h, FrequencySeries):
117 h = h.ifft()
118 h = h.to_lal()
119 data = data.to_lal()
120 lalsim.SimAddInjectionREAL8TimeSeries(data, h, response)
121 data = TimeSeries.from_lal(data)
122 return data
123
124
125class ModePair(NamedTuple):
126 """
127 Returns a named tuple given the l,m values
128 """
129 l: int
130 m: int
131
132
134 """
135 Class to return spin `s` weighted spherical harmonic given ModePair
136 """
137
138 def __new__(cls, s, l, m):
139 new = super().__new__(cls, l, m)
140 if new.l < abs(s):
141 raise ValueError('Require l >= |s|')
142 if abs(new.m) > new.l:
143 raise ValueError('Require |m| <= l')
144 new.s = s
145 return new
146
147 def __call__(self, theta, phi):
148 theta = Angle(theta, u.rad)
149 phi = Angle(phi, u.rad)
150 return lal.SpinWeightedSphericalHarmonic(theta.rad, phi.rad, self.s, self.l, self.m)
151
152
154 """
155 Class to return spin `-2` weighted spherical harmonic given ModePair
156 """
157 def __new__(cls, l, m):
158 return super().__new__(cls, -2, l, m)
159
160
161class GravitationalWaveModes(Dict[SpinWeightedSphericalHarmonicMode, Union[TimeSeries, FrequencySeries]]):
162 """
163 Class for gravitational wave modes which returns the waveform recomposed with -2 spin weighted spherical harmonics
164 given a (theta, phi) value
165 """
166 def __call__(self, theta, phi):
167 """
168 Return plus and cross polarizations from the gravitational wave modes.
169
170 Parameters
171 ----------
172 theta : `~astropy.units.Quantity`
173 Theta angle (inclination, theta_jn) in units of rad.
174 phi : `~astropy.units.Quantity`
175 Phi angle (inclination, theta_jn) in units of rad.
176 """
177 if isinstance(theta, float) and isinstance(phi, float):
178 raise TypeError("Theta and phi should be in units of astropy.units.rad, not float")
179 elif theta.unit.is_equivalent(u.rad) and phi.unit.is_equivalent(u.rad):
180 pass
181 else:
182 raise TypeError("Theta and phi should be in units of astropy.units.rad")
183
184
185 hpc = 0.
186 hp = 0.
187 hc = 0.
188 for key, hlm in self.items():
189 if isinstance(key, str):
190 pass
191 elif isinstance(hlm, TimeSeries):
192 mode = TensorSphericalHarmonicMode(int(key[0]), int(key[1]))
193 hpc += mode(theta, phi) * hlm
194 hp = hpc.real
195 hc = -hpc.imag
196
197 elif isinstance(hlm, FrequencySeries):
198 # Recombination of freq domain hp/hc from here :
199 # https://git.ligo.org/lscsoft/lalsuite/-/blob/master/lalsimulation/lib/LALSimInspiral.c#L3477
200 l, m = int(key[0]), int(key[1])
201 mode = TensorSphericalHarmonicMode(l, m)
202 ylm = mode(theta, phi)
203 hp += 0.5*( ylm * hlm + np.conj(ylm) * np.conj(hlm)[::-1])
204 hc += 1j*0.5*( ylm * hlm - np.conj(ylm)* np.conj(hlm)[::-1])
205
206 return GravitationalWavePolarizations(hp, hc)
Class for gravitational wave modes which returns the waveform recomposed with -2 spin weighted spheri...
Definition: gw.py:165
def __call__(self, theta, phi)
Return plus and cross polarizations from the gravitational wave modes.
Definition: gw.py:176
def strain(self, det, ra, dec, psi, tgps)
Compute the detector strain in zero-noise.
Definition: gw.py:51
def add_strain(self, data, det, ra, dec, psi, tgps, response=None)
Add strain to some LAL strain as required.
Definition: gw.py:112
Returns a named tuple given the l,m values.
Definition: gw.py:128
Class to return spin s weighted spherical harmonic given ModePair.
Definition: gw.py:136
Class to return spin -2 weighted spherical harmonic given ModePair.
Definition: gw.py:156