Repository navigation
Conversation
ColmTalbot
left a comment
There was a problem hiding this comment.
This is a nice change, I think we'll need to think a little about the best way to do this. I've suggested waiting a while to get this in as I don't think it is urgent, the labelling of ET2/ET3 doesn't really matter, and we can make the changes more satisfyingly if we break things.
| * 180 | ||
| / np.pi | ||
| ) | ||
| unit_vector_x = self[ii].geometry.unit_vector_along_arm("x") |
There was a problem hiding this comment.
I know this is not the change for the current code, but given that this is clearly better thought out than the current method, can the logic for updating the geometry be moved into a standalone function (with a nice docstring)?
There was a problem hiding this comment.
Perhaps we could add setters to the InterferometerGeometry class for x, y and vertex? For the lat, long, elevation, tilt and azimuth these setters are separate but it does not seem sensible to me that someone can change only one Cartesian coordinate.
All the update logic could then be moved into the geometry class and only the calculation of the new corner points (which is triangle specific) remains in the 'TriangularInterferometer` class.
My main concern with this is approach is that one would need to be careful to update the vertex position before changing the unit vector. Otherwise the calculation of tilt and azimuth is wrong. This could be avoided by making the unit vectors the "base information" and deriving the tilt and azimuth from it.
This also raises the question of whether the constructor should be adapted to allow users to define the detector directly from Cartesian parameters. I guess you could argue that this is actually the only way you should be able to set these parameters as interferometers are constant.
| yarm_azimuth, | ||
| xarm_tilt=0.0, | ||
| yarm_tilt=0.0, | ||
| clockwise=True, |
There was a problem hiding this comment.
I would personally vote for just making a breaking change here (and waiting for a major release) to avoid adding a new parameter that doesn't really mean anything (the set of three Interferometers are just a permutation of each other with the different directions).
There was a problem hiding this comment.
Fine by me, if people really want they can still override the detector names
| return np.array([x_comp, y_comp, z_comp]) | ||
|
|
||
|
|
||
| def get_vertex_position_ellipsoid(x_comp, y_comp, z_comp): |
There was a problem hiding this comment.
I feel like this function should take and recieve the fame type, either (float, float, float) -> (float, float, float) or array -> array. I don't really mind which, but it would be good to be consistent.
I now see that the same thing happens in get_vertex_position_geocentric. sigh, I'm not sure if it is worth changing the other function.
We could change the other function to something like
def get_vertex_position_geocentric(position, _longitude=None, _elevation=None):
if longitude is not None and not isarray(position):
WARN("... syntax changed in Bilby 3.0.0 ...")
position = np.array([position, _longitude, _elevation])
...This would need a wider set of eyes/opinions.
There was a problem hiding this comment.
I went through the exact same thought process when writing the function. This seems like a good compromise.
|
Now we're preparing for 3.0, I think we can more easily consider the breaking changes mentioned above. @milanwils @ColmTalbot can you revisit this and assess where we stand? |
|
@mj-will Sorry for the delay in replying. I had to refresh my memory a bit because I made this request quite some time ago. As far as I can see, the fix in #1016 solved most of the issues but to be entirely correct, the changes in this PR should still be applied because:
I believe there are just a few questions to be answered and then we can proceed rather quickly:
|
|
Thanks @milanwils, here are my thoughts.
Yes, I think so.
Agree, this would be a breaking change but that's fine for 3.0
Let's start with that and see
My concern with astropy would be speed. Do you know how they compare? If your implementation is faster then a test to show they're equivalent may be enough.
I've not looked at the PR in detail, but if I'm following correctly, you're referring to the large block of code that's been added to the the initialiser. My preference would be to try and factor this out into a method (or methods) so it's easier to test in isolation but this might need some more thought. |
|
I have pushed changes for 1-3.
|
|
I agree with all Michael's comments. The other reason not to use astropy is that they don't yet support array backend flexibility. We should add that to this change. It should be straightforward as the scipy functionality we use is supported, so it should just be sprinkling to xp. We have docs here and I'm happy to give specific pointers if that's better, it will just take a little while. |
3acb12f to
97cc3cd
Compare
|
I took a look at the array backend flexibility and it should indeed not be hard to implement. To test that everything is working I have to either rebase onto or merge main into this feature branch because the branches diverted before backend flexibility was added to Bilby. What is the standard on this project? |
|
I usually try to rebase, but if it is painful, I just merge. We usually squash commits anyway before we merge anyway, so it shouldn't really matter. |
|
I went with a rebase because it was quite painful. I stuck with my own implementation for the coordinate transform, moved the update logic to a member function of the TriangularInterferometer class (the update is specific to such a design) and I have added some array back-end flexibility code that works to the best of my knowledge. For the operations on scalars, I left in the numpy code. As far as I can see, all requested changes are now included. Let me know if there are more changes I have to make. I will still verify that LAL and Bilby are now in perfect alignment. Are there additional tests we want to add, e.g. is the sum of the antenna patterns zero? |
|
test_et_lal_consistency.py |
ColmTalbot
left a comment
There was a problem hiding this comment.
Thanks! Just a few comments about the array backend stuff.
| semi_major_axis = 6378137 # for ellipsoid model of Earth, in m | ||
| semi_minor_axis = 6356752.314 # in m | ||
|
|
||
| r = np.sqrt(position[0] ** 2 + position[1] ** 2) |
There was a problem hiding this comment.
All of the uses of np in this function (and the others) should be xp.
| next_vertex_ellipsoid = get_vertex_position_ellipsoid(next_vertex_geocentric) | ||
| next_latitude_rad, next_longitude_rad, next_elevation = next_vertex_ellipsoid | ||
|
|
||
| rotation_vector = np.cross(unit_vector_x, unit_vector_y) |
There was a problem hiding this comment.
As with other cases, these should all be xp.
| if not self._vertex_updated: | ||
| self._vertex = gwutils.get_vertex_position_geocentric(self._latitude, self._longitude, | ||
| self.elevation) | ||
| self._vertex = gwutils.get_vertex_position_geocentric(np.array([self._latitude, self._longitude, |
There was a problem hiding this comment.
I'm not sure the best way to find the appropriate array backend here, but I would guess we want this import at the top of the file
from ...compat.utils import array_modulethen here we would have
xp = array_module((self._latitude, self._longitude, self.elevation))There was a problem hiding this comment.
I have decided to take the approach mentioned in the commit message:
To infer the correct array backend in
InterferometerGeometry.vertex,
the propertyarray_backendwas added in line with the implementations
inInterferometerandInterferometerList. This also makes the
Interferometer.array_backendmethod independent of implementation
details inInterferometerGeometry.
I believe this is the cleanest solution, but if you prefer something else then that is fine by me too.
Unrelated question: is there is reason the methods like set_array_backend are used rather than a decorated
@array_backend.setter
def array_backend(self, xp):
<implementation details>
like there is in many other parts of the code?
There was a problem hiding this comment.
No, I guess just a moment of madness on my side, we should probably change this to be a setter as you suggest.
To infer the correct array backend in `InterferometerGeometry.vertex`, the property `array_backend` was added in line with the implementations in `Interferometer` and `InterferometerList`. This also makes the `Interferometer.array_backend` method independent of implementation details in `InterferometerGeometry`.
ColmTalbot
left a comment
There was a problem hiding this comment.
Thanks, just one last place where there is an np instead of xp.
| if not self._vertex_updated: | ||
| self._vertex = gwutils.get_vertex_position_geocentric(self._latitude, self._longitude, | ||
| self.elevation) | ||
| self._vertex = gwutils.get_vertex_position_geocentric(np.array([self._latitude, self._longitude, |
There was a problem hiding this comment.
No, I guess just a moment of madness on my side, we should probably change this to be a setter as you suggest.
Co-authored-by: Colm Talbot <talbotcolm@gmail.com>
|
Sorry for missing that np call. This time I checked that at least I did not introduce any new calls to np in this PR by running I also changed the setter because it was not to much work. If you prefer that in a separate PR then I'll revert the commit and open a new PR. |
ColmTalbot
left a comment
There was a problem hiding this comment.
Thanks @milanwils! As long as the tests pass, I'm happy with this now.
| if not self._vertex_updated: | ||
| self._vertex = gwutils.get_vertex_position_geocentric(self._latitude, self._longitude, | ||
| self.elevation) | ||
| self._vertex = gwutils.get_vertex_position_geocentric(xp.array([self._latitude, self._longitude, |
There was a problem hiding this comment.
I should have caught these, they should all be xp.asarray to appease torch.
|
@ColmTalbot Sorry, I missed the line in the contributing guideline about testing with multiple backends, I thought this was done automatically. I pushed the changes . |
One test was failing in `gw/utils.py` because `torch.deg2rad` only accepts Tensor and not a float. This is fixed by turning the argument into an array using `xp.asarray`. Additionally, the test used assertAlmostEqual which requires the builtin function `__round__` which is not defined for `Tensor` objects. Therefore, the tensors are cast into float before comparing.
|
@ColmTalbot I made some changes in the test so that it also works for pytorch which forces Tensor inputs (no float) and does not have the builtin round. |
ColmTalbot
left a comment
There was a problem hiding this comment.
LGTM, thanks for pushing this @milanwils
Since bilby-dev/bilby#1016 each next channel sits at the end of the current channel's x arm, but the arm azimuths are still rotated by 240 degrees, so the second and third channels have one arm 60 degrees off the triangle (and swapped labels with respect to LAL). Follow LAL (LIGO-T1400308) and bilby-dev/bilby#894 instead: step along the straight x arm on the WGS84 ellipsoid and rotate both arms by 120 degrees about the detector normal, with azimuths, tilts and elevation recomputed in the next vertex's local frame. The colocated option rotates the arms in the same way about the first vertex. The geocentric-to-geodetic inversion uses |z| and real cube roots, so it also holds in the southern hemisphere, where #894's version returns NaN. For the ET-EMR v70 injection this swaps the ET-EMR2/3 SNRs and changes the weaker one from 162.30 to 160.84. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
As mentioned in #713, the current geometry of the ET is not in line with its definition in LAL.
Proposed Solution
ET.interferometerfile toclockwise=Falseso that the default is consistent with LAL. If this is undesired for backwards compatibility, it could be set toclockwise=True.Additional information
. Only if both get accepted do the configurations become identical.
There is also a small error in the LAL config, see LAL PR