Coverage for bilby/gw/detector/geometry.py: 98%
121 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-06 04:57 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-06 04:57 +0000
1import numpy as np
2from bilby_cython.geometry import calculate_arm, detector_tensor
4from .. import utils as gwutils
7class InterferometerGeometry(object):
8 def __init__(self, length, latitude, longitude, elevation, xarm_azimuth, yarm_azimuth,
9 xarm_tilt=0., yarm_tilt=0.):
10 """
11 Instantiate an Interferometer object.
13 Parameters
14 ==========
16 length: float
17 Length of the interferometer in km.
18 latitude: float
19 Latitude North in degrees (South is negative).
20 longitude: float
21 Longitude East in degrees (West is negative).
22 elevation: float
23 Height above surface in metres.
24 xarm_azimuth: float
25 Orientation of the x arm in degrees North of East.
26 yarm_azimuth: float
27 Orientation of the y arm in degrees North of East.
28 xarm_tilt: float, optional
29 Tilt of the x arm in radians above the horizontal defined by
30 ellipsoid earth model in LIGO-T980044-08.
31 yarm_tilt: float, optional
32 Tilt of the y arm in radians above the horizontal.
33 """
35 self._x_updated = False
36 self._y_updated = False
37 self._vertex_updated = False
38 self._detector_tensor_updated = False
40 self.length = length
41 self.latitude = latitude
42 self.longitude = longitude
43 self.elevation = elevation
44 self.xarm_azimuth = xarm_azimuth
45 self.yarm_azimuth = yarm_azimuth
46 self.xarm_tilt = xarm_tilt
47 self.yarm_tilt = yarm_tilt
48 self._vertex = None
49 self._x = None
50 self._y = None
51 self._detector_tensor = None
53 def __eq__(self, other):
54 for attribute in ['length', 'latitude', 'longitude', 'elevation',
55 'xarm_azimuth', 'yarm_azimuth', 'xarm_tilt', 'yarm_tilt']:
56 if not getattr(self, attribute) == getattr(other, attribute):
57 return False
58 return True
60 def __repr__(self):
61 return self.__class__.__name__ + '(length={}, latitude={}, longitude={}, elevation={}, ' \
62 'xarm_azimuth={}, yarm_azimuth={}, xarm_tilt={}, yarm_tilt={})' \
63 .format(float(self.length), float(self.latitude), float(self.longitude),
64 float(self.elevation), float(self.xarm_azimuth), float(self.yarm_azimuth), float(self.xarm_tilt),
65 float(self.yarm_tilt))
67 @property
68 def latitude(self):
69 """ Saves latitude in rad internally. Updates related quantities if set to a different value.
71 Returns
72 =======
73 float: The latitude position of the detector in degree
74 """
75 return self._latitude * 180 / np.pi
77 @latitude.setter
78 def latitude(self, latitude):
79 self._latitude = latitude * np.pi / 180
80 self._x_updated = False
81 self._y_updated = False
82 self._vertex_updated = False
84 @property
85 def latitude_radians(self):
86 """
87 Returns
88 =======
89 float: The latitude position of the detector in radians
90 """
92 return self._latitude
94 @property
95 def longitude(self):
96 """ Saves longitude in rad internally. Updates related quantities if set to a different value.
98 Returns
99 =======
100 float: The longitude position of the detector in degree
101 """
102 return self._longitude * 180 / np.pi
104 @longitude.setter
105 def longitude(self, longitude):
106 self._longitude = longitude * np.pi / 180
107 self._x_updated = False
108 self._y_updated = False
109 self._vertex_updated = False
111 @property
112 def longitude_radians(self):
113 """
114 Returns
115 =======
116 float: The latitude position of the detector in radians
117 """
119 return self._longitude
121 @property
122 def elevation(self):
123 """ Updates related quantities if set to a different values.
125 Returns
126 =======
127 float: The height about the surface in meters
128 """
129 return self._elevation
131 @elevation.setter
132 def elevation(self, elevation):
133 self._elevation = elevation
134 self._vertex_updated = False
136 @property
137 def xarm_azimuth(self):
138 """ Saves the x-arm azimuth in rad internally. Updates related quantities if set to a different values.
140 Returns
141 =======
142 float: The x-arm azimuth in degrees.
144 """
145 return self._xarm_azimuth * 180 / np.pi
147 @xarm_azimuth.setter
148 def xarm_azimuth(self, xarm_azimuth):
149 self._xarm_azimuth = xarm_azimuth * np.pi / 180
150 self._x_updated = False
152 @property
153 def yarm_azimuth(self):
154 """ Saves the y-arm azimuth in rad internally. Updates related quantities if set to a different values.
156 Returns
157 =======
158 float: The y-arm azimuth in degrees.
160 """
161 return self._yarm_azimuth * 180 / np.pi
163 @yarm_azimuth.setter
164 def yarm_azimuth(self, yarm_azimuth):
165 self._yarm_azimuth = yarm_azimuth * np.pi / 180
166 self._y_updated = False
168 @property
169 def xarm_tilt(self):
170 """ Updates related quantities if set to a different values.
172 Returns
173 =======
174 float: The x-arm tilt in radians.
176 """
177 return self._xarm_tilt
179 @xarm_tilt.setter
180 def xarm_tilt(self, xarm_tilt):
181 self._xarm_tilt = xarm_tilt
182 self._x_updated = False
184 @property
185 def yarm_tilt(self):
186 """ Updates related quantities if set to a different values.
188 Returns
189 =======
190 float: The y-arm tilt in radians.
192 """
193 return self._yarm_tilt
195 @yarm_tilt.setter
196 def yarm_tilt(self, yarm_tilt):
197 self._yarm_tilt = yarm_tilt
198 self._y_updated = False
200 @property
201 def vertex(self):
202 """ Position of the IFO vertex in geocentric coordinates in meters.
204 Is automatically updated if related quantities are modified.
206 Returns
207 =======
208 array_like: A 3D array representation of the vertex
209 """
210 if not self._vertex_updated:
211 self._vertex = gwutils.get_vertex_position_geocentric(self._latitude, self._longitude,
212 self.elevation)
213 self._vertex_updated = True
214 return self._vertex
216 @property
217 def x(self):
218 """ A unit vector along the x-arm
220 Is automatically updated if related quantities are modified.
222 Returns
223 =======
224 array_like: A 3D array representation of a unit vector along the x-arm
226 """
227 if not self._x_updated:
228 self._x = self.unit_vector_along_arm('x')
229 self._x_updated = True
230 self._detector_tensor_updated = False
231 return self._x
233 @property
234 def y(self):
235 """ A unit vector along the y-arm
237 Is automatically updated if related quantities are modified.
239 Returns
240 =======
241 array_like: A 3D array representation of a unit vector along the y-arm
243 """
244 if not self._y_updated:
245 self._y = self.unit_vector_along_arm('y')
246 self._y_updated = True
247 self._detector_tensor_updated = False
248 return self._y
250 @property
251 def detector_tensor(self):
252 """
253 Calculate the detector tensor from the unit vectors along each arm of the detector.
255 See Eq. B6 of arXiv:gr-qc/0008066
257 Is automatically updated if related quantities are modified.
259 Returns
260 =======
261 array_like: A 3x3 array representation of the detector tensor
263 """
264 if not self._x_updated or not self._y_updated:
265 _, _ = self.x, self.y # noqa
266 if not self._detector_tensor_updated:
267 self._detector_tensor = detector_tensor(x=self.x, y=self.y)
268 self._detector_tensor_updated = True
269 return self._detector_tensor
271 def unit_vector_along_arm(self, arm):
272 """
273 Calculate the unit vector pointing along the specified arm in cartesian Earth-based coordinates.
275 See Eqs. B14-B17 in arXiv:gr-qc/0008066
277 Parameters
278 ==========
279 arm: str
280 'x' or 'y' (arm of the detector)
282 Returns
283 =======
284 array_like: 3D unit vector along arm in cartesian Earth-based coordinates
286 Raises
287 ======
288 ValueError: If arm is neither 'x' nor 'y'
290 """
291 if arm == 'x':
292 return calculate_arm(
293 arm_tilt=self._xarm_tilt,
294 arm_azimuth=self._xarm_azimuth,
295 longitude=self._longitude,
296 latitude=self._latitude
297 )
298 elif arm == 'y':
299 return calculate_arm(
300 arm_tilt=self._yarm_tilt,
301 arm_azimuth=self._yarm_azimuth,
302 longitude=self._longitude,
303 latitude=self._latitude
304 )
305 else:
306 raise ValueError("Arm must either be 'x' or 'y'.")