Skip to content

Commit 2994591

Browse files
Merge pull request #97 from HERA-Team/prefix-bcrs
perf: ability to pre-fix the BCRS coordinates
2 parents 1dfd608 + 5c7dc96 commit 2994591

6 files changed

Lines changed: 116 additions & 57 deletions

File tree

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ install_requires =
2525
line-profiler
2626
numpy>=2.0
2727
psutil
28-
pyuvdata@git+https://github.com/RadioAstronomySoftwareGroup/pyuvdata
28+
pyuvdata>=3.1.0
2929
rich
3030
scipy
3131
python_requires = >=3.9

src/matvis/cli.py

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
from line_profiler import LineProfiler
2222
from pathlib import Path
2323
from pyuvdata import UVBeam
24+
from pyuvdata.analytic_beam import GaussianBeam
2425
from pyuvdata.telescopes import get_telescope
25-
from pyuvsim import AnalyticBeam, simsetup
2626
from rich.console import Console
2727
from rich.logging import RichHandler
2828
from rich.rule import Rule
@@ -382,7 +382,16 @@ def get_stats_and_lines(filename, start_lineno, timings, time_unit):
382382

383383

384384
def get_standard_sim_params(
385-
use_analytic_beam: bool, nfreq, ntime, nants, nsource, nbeams, naz=360, nza=180
385+
use_analytic_beam: bool,
386+
nfreq,
387+
ntime,
388+
nants,
389+
nsource,
390+
nbeams,
391+
naz=360,
392+
nza=180,
393+
freq_min=100e6,
394+
freq_max=200e6,
386395
):
387396
"""Create some standard random simulation parameters for use in profiling.
388397
@@ -391,24 +400,17 @@ def get_standard_sim_params(
391400
# Set the seed so that different runs take about the same time.
392401
rng = np.random.default_rng()
393402

403+
# Source locations and frequencies
404+
freqs = np.linspace(freq_min, freq_max, nfreq)
405+
394406
# Beam model
395-
if use_analytic_beam:
396-
beam = AnalyticBeam("gaussian", diameter=14.0)
397-
else:
398-
# This is a peak-normalized e-field beam file at 100 and 101 MHz,
399-
# downsampled to roughly 4 square-degree resolution.
400-
beam = UVBeam()
401-
beam.read_beamfits(beam_file, use_future_array_shapes=True)
402-
403-
# Up/down sample the beam
404-
beam.interpolation_function = "az_za_simple"
405-
beam = beam.interp(
406-
az_array=np.linspace(0, 2 * np.pi, naz + 1)[:-1],
407-
za_array=np.linspace(0, np.pi, nza + 1),
408-
freq_array=beam.freq_array,
409-
reuse_spline=True,
410-
new_object=True,
411-
az_za_grid=True,
407+
beam = GaussianBeam(diameter=14.0)
408+
409+
if not use_analytic_beam:
410+
beam = beam.to_uvbeam(
411+
freq_array=freqs,
412+
axis1_array=np.linspace(0, 2 * np.pi, naz + 1)[:-1],
413+
axis2_array=np.linspace(0, np.pi, nza + 1),
412414
)
413415

414416
beams = [beam] * nbeams
@@ -445,9 +447,6 @@ def get_standard_sim_params(
445447
flux0 = np.random.random(nsource) * 4
446448
spec_indx = np.random.normal(0.8, scale=0.05, size=nsource)
447449

448-
# Source locations and frequencies
449-
freqs = np.linspace(100e6, 200e6, nfreq)
450-
451450
# Calculate source fluxes for matvis
452451
flux = ((freqs[:, np.newaxis] / freqs[0]) ** spec_indx.T * flux0.T).T
453452

src/matvis/core/coords.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ def __init__(
6767

6868
self.chunk_size = chunk_size or self.nsrc
6969
self.source_buffer = source_buffer
70-
self.nsrc_alloc = int(self.chunk_size * self.source_buffer)
70+
if self.chunk_size > 1000:
71+
self.nsrc_alloc = int(self.chunk_size * self.source_buffer)
72+
else:
73+
self.nsrc_alloc = self.chunk_size
7174

7275
def setup(self):
7376
"""Allocate memory for the rotation."""

src/matvis/cpu/coords.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,27 @@ def __init__(self, update_bcrs_every=0.0, *args, **kwargs):
6464
super().__init__(*args, **kwargs)
6565
self.update_bcrs_every = update_bcrs_every * un.s
6666

67-
def setup(self):
68-
"""Standard setup, as well as storing the cartesian representation of ECI."""
69-
super().setup()
67+
# Do one rotation to warm up the cache. This reads the IERS table.
68+
frame = AltAz(obstime=self.times[0], location=self.telescope_loc)
69+
self.skycoords[0].transform_to(frame)
70+
self._time_of_last_evaluation = None
71+
72+
# These are unchanging over time, so init them outside the setup() to save
73+
# memory when multi-processing.
7074
self._eci = self.xp.asarray(
7175
point_source_crd_eq(self.skycoords.ra, self.skycoords.dec)
7276
)
7377

78+
def setup(self):
79+
"""Standard setup, as well as storing the cartesian representation of ECI."""
80+
super().setup()
81+
7482
# BCRS holds the deflected, aberrated bnp-d coordinates, which don't change
7583
# significantly over time.
76-
self._bcrs = self._eci.copy()
77-
self._time_of_last_evaluation = None
78-
79-
# Do one rotation to warm up the cache.
80-
frame = AltAz(obstime=self.times[0], location=self.telescope_loc)
81-
self.skycoords[0].transform_to(frame)
84+
if not hasattr(self, "_bcrs"):
85+
self._bcrs = self.xp.full(
86+
self._eci.shape, dtype=self._eci.dtype, fill_value=0.0
87+
)
8288

8389
def _atioq(self, xyz: np.ndarray, astrom):
8490
# cirs to hadec rot
@@ -177,21 +183,22 @@ def _apco(self, observed_frame):
177183
def _get_obsf(self, obstime, location):
178184
return AltAz(obstime=obstime, location=location)
179185

180-
def rotate(self, t: int) -> tuple[np.ndarray, np.ndarray]:
181-
"""Rotate the coordinates into the observed frame."""
182-
obsf = self._get_obsf(self.times[t], self.telescope_loc)
183-
astrom = self._apco(obsf)
184-
185-
# Copy the eci coordinates, because these routines modify them in-place
186-
186+
def _set_bcrs(self, t, astrom=None):
187187
# convert to topocentric CIRS
188188
# together, _ld + _ab take ~90% of the time.
189+
if astrom is None:
190+
obsf = self._get_obsf(self.times[t], self.telescope_loc)
191+
astrom = self._apco(obsf)
192+
189193
if (
190194
self._time_of_last_evaluation is None
191195
or self.times[t] - self.times[self._time_of_last_evaluation]
192196
> self.update_bcrs_every
193197
):
194-
self._bcrs[:] = self._eci[:]
198+
if hasattr(self, "_bcrs"):
199+
self._bcrs[:] = self._eci[:]
200+
else:
201+
self._bcrs = self._eci.copy()
195202

196203
# Light deflection by the Sun, giving BCRS natural direction.
197204
self._ld(self._bcrs, self.xp.asarray(astrom["eh"]), astrom["em"], 1e-6)
@@ -207,6 +214,13 @@ def rotate(self, t: int) -> tuple[np.ndarray, np.ndarray]:
207214

208215
self._time_of_last_evaluation = t
209216

217+
def rotate(self, t: int) -> tuple[np.ndarray, np.ndarray]:
218+
"""Rotate the coordinates into the observed frame."""
219+
obsf = self._get_obsf(self.times[t], self.telescope_loc)
220+
astrom = self._apco(obsf)
221+
222+
self._set_bcrs(t, astrom)
223+
210224
self.all_coords_topo[:] = self._bcrs[:]
211225

212226
# now perform observed conversion

src/matvis/cpu/cpu.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -157,20 +157,8 @@ def simulate(
157157
precision,
158158
source_buffer=source_buffer,
159159
)
160-
nsrc_alloc = int(npixc * source_buffer)
161-
162-
bmfunc = UVBeamInterpolator(
163-
beam_list=beam_list,
164-
beam_idx=beam_idx,
165-
polarized=polarized,
166-
nant=nant,
167-
freq=freq,
168-
spline_opts=beam_spline_opts,
169-
precision=precision,
170-
nsrc=nsrc_alloc,
171-
)
172-
173160
coord_method = CoordinateRotation._methods[coord_method]
161+
174162
coord_method_params = coord_method_params or {}
175163
coords = coord_method(
176164
flux=np.sqrt(0.5 * I_sky),
@@ -182,6 +170,19 @@ def simulate(
182170
source_buffer=source_buffer,
183171
**coord_method_params,
184172
)
173+
174+
nsrc_alloc = coords.nsrc_alloc
175+
bmfunc = UVBeamInterpolator(
176+
beam_list=beam_list,
177+
beam_idx=beam_idx,
178+
polarized=polarized,
179+
nant=nant,
180+
freq=freq,
181+
spline_opts=beam_spline_opts,
182+
precision=precision,
183+
nsrc=nsrc_alloc,
184+
)
185+
185186
taucalc = TauCalculator(
186187
antpos=antpos, freq=freq, precision=precision, nsrc=nsrc_alloc
187188
)

tests/test_coordrot.py

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from matvis import HAVE_GPU
1212
from matvis.core.coords import CoordinateRotation
13-
from matvis.cpu.coords import CoordinateRotationAstropy
13+
from matvis.cpu.coords import CoordinateRotationAstropy, CoordinateRotationERFA
1414

1515
if HAVE_GPU:
1616
import cupy as cp
@@ -35,7 +35,7 @@ def get_angles(x, y):
3535
return xp.arccos(ratio)
3636

3737

38-
def get_random_coordrot(n, method, gpu, seed, precision=2):
38+
def get_random_coordrot(n, method, gpu, seed, precision=2, setup: bool = True, **kw):
3939
"""Get a random coordinate rotation object."""
4040
rng = np.random.default_rng(seed)
4141
location = get_telescope("hera").location
@@ -51,8 +51,10 @@ def get_random_coordrot(n, method, gpu, seed, precision=2):
5151
skycoords=skycoords,
5252
gpu=gpu,
5353
precision=precision,
54+
**kw
5455
)
55-
coords.setup()
56+
if setup:
57+
coords.setup()
5658
return coords
5759

5860

@@ -101,3 +103,43 @@ def test_accuracy_against_astropy(method, gpu, precision):
101103
np.testing.assert_allclose(angles, 0, atol=0.01) # 10 mas
102104
else:
103105
np.testing.assert_allclose(angles, 0, atol=150) # 50 mas
106+
107+
108+
def test_coord_rot_erfa_set_bcrs():
109+
"""Test that setting bcrs before setup works as expected."""
110+
normal = get_random_coordrot(
111+
1000, CoordinateRotationERFA, gpu=False, seed=1, precision=1
112+
)
113+
bcrs = get_random_coordrot(
114+
1000, CoordinateRotationERFA, gpu=False, seed=1, precision=1, setup=False
115+
)
116+
bcrs._set_bcrs(0)
117+
bcrs.setup()
118+
119+
normal.rotate(0)
120+
bcrs.rotate(0)
121+
122+
np.testing.assert_allclose(normal.all_coords_topo, bcrs.all_coords_topo)
123+
124+
125+
def test_larger_chunksize():
126+
"""Test that using different chunk sizes results in the same output."""
127+
small = get_random_coordrot(
128+
10000, CoordinateRotationERFA, gpu=False, seed=1, precision=1, chunk_size=100
129+
)
130+
large = get_random_coordrot(
131+
10000, CoordinateRotationERFA, gpu=False, seed=1, precision=1, chunk_size=5000
132+
)
133+
default = get_random_coordrot(
134+
10000, CoordinateRotationERFA, gpu=False, seed=1, precision=1
135+
)
136+
small.select_chunk(0)
137+
large.select_chunk(0)
138+
default.select_chunk(0)
139+
140+
np.testing.assert_allclose(
141+
small.coords_above_horizon, large.coords_above_horizon[:, :100]
142+
)
143+
np.testing.assert_allclose(
144+
small.coords_above_horizon, default.coords_above_horizon[:, :100]
145+
)

0 commit comments

Comments
 (0)