Skip to content

Commit c6cd13c

Browse files
committed
Restructure unit tests. Compute global coverage on the sky
when autoscaling. Several unrelated small fixes.
1 parent 3abb45f commit c6cd13c

File tree

6 files changed

+249
-124
lines changed

6 files changed

+249
-124
lines changed

src/toast/ops/noise_estimation.py

+15-14
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ class NoiseEstim(Operator):
128128
)
129129

130130
output_dir = Unicode(
131-
".",
131+
None,
132+
allow_none=True,
132133
help="If specified, write output data products to this directory",
133134
)
134135

@@ -1075,21 +1076,21 @@ def process_noise_estimate(
10751076
)
10761077
log.debug_rank("Discard outliers", timer=timer)
10771078

1078-
self.save_psds(
1079-
binfreq, all_psds, all_times, det1, det2, fsample, fileroot, all_cov
1080-
)
1081-
1082-
if nbad > 0:
1079+
if self.output_dir is not None:
10831080
self.save_psds(
1084-
binfreq,
1085-
good_psds,
1086-
good_times,
1087-
det1,
1088-
det2,
1089-
fsample,
1090-
fileroot + "_good",
1091-
good_cov,
1081+
binfreq, all_psds, all_times, det1, det2, fsample, fileroot, all_cov
10921082
)
1083+
if nbad > 0:
1084+
self.save_psds(
1085+
binfreq,
1086+
good_psds,
1087+
good_times,
1088+
det1,
1089+
det2,
1090+
fsample,
1091+
fileroot + "_good",
1092+
good_cov,
1093+
)
10931094

10941095
final_freqs = binfreq
10951096
final_psd = np.mean(np.array(good_psds), axis=0)

src/toast/ops/noise_model.py

+43-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import numpy as np
66
import traitlets
77
from astropy import units as u
8-
from scipy.optimize import Bounds, least_squares
8+
from scipy.optimize import least_squares
99

1010
from ..noise import Noise
1111
from ..noise_sim import AnalyticNoise
@@ -105,8 +105,12 @@ def _exec(self, data, detectors=None, **kwargs):
105105
freqs = in_model.freq(det)
106106
in_psd = in_model.psd(det)
107107
fitted, result = self._fit_psd(freqs, in_psd, params)
108-
if result.success:
108+
if result.success and len(result.x) == 3:
109+
# This was a good fit
109110
params = result.x
111+
else:
112+
msg = f"FitNoiseModel observation {ob.name}, det {det} failed. Params = {result.x}"
113+
log.warning(msg)
110114
nse_freqs[det] = freqs
111115
nse_psds[det] = fitted
112116
if ob.comm.comm_group is not None:
@@ -156,12 +160,18 @@ def _evaluate_model(self, freqs, fmin, net, fknee, alpha):
156160
return psd
157161

158162
def _fit_fun(self, x, *args, **kwargs):
159-
net = x[0]
160-
fknee = x[1]
161-
alpha = x[2]
162163
freqs = kwargs["freqs"]
163164
data = kwargs["data"]
164165
fmin = kwargs["fmin"]
166+
if "net" in kwargs:
167+
# We are fixing the NET value
168+
net = kwargs["net"]
169+
fknee = x[0]
170+
alpha = x[1]
171+
else:
172+
net = x[0]
173+
fknee = x[1]
174+
alpha = x[2]
165175
current = self._evaluate_model(freqs, fmin, net, fknee, alpha)
166176
# We weight the residual so that the high-frequency values specifying
167177
# the white noise plateau / NET are more important. Also the lowest
@@ -186,6 +196,9 @@ def _fit_psd(self, freqs, data, guess=None):
186196
result = least_squares(
187197
self._fit_fun,
188198
x_0,
199+
xtol=1.0e-8,
200+
gtol=1.0e-8,
201+
ftol=1.0e-8,
189202
kwargs={"freqs": raw_freqs, "data": raw_data, "fmin": raw_fmin},
190203
)
191204
fit_data = data
@@ -196,6 +209,31 @@ def _fit_psd(self, freqs, data, guess=None):
196209
)
197210
* psd_unit
198211
)
212+
# else:
213+
# # Try fixing the NET based on the last few elements
214+
# fixed_net = np.sqrt(np.mean(raw_data[-5:]))
215+
# x_0 = np.array([0.1, 1.0])
216+
# result = least_squares(
217+
# self._fit_fun,
218+
# x_0,
219+
# xtol=1.0e-8,
220+
# gtol=1.0e-8,
221+
# ftol=1.0e-8,
222+
# kwargs={
223+
# "freqs": raw_freqs,
224+
# "data": raw_data,
225+
# "fmin": raw_fmin,
226+
# "net": fixed_net,
227+
# },
228+
# )
229+
# if result.success:
230+
# fit_data = (
231+
# self._evaluate_model(
232+
# raw_freqs, raw_fmin, fixed_net, result.x[0], result.x[1]
233+
# )
234+
# * psd_unit
235+
# )
236+
199237
return fit_data, result
200238

201239
def _finalize(self, data, **kwargs):

src/toast/ops/pixels_wcs.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ def _set_wcs(self, proj, center, bounds, dims, res):
282282
self.pix_ra = self.wcs_shape[0]
283283
self.pix_dec = self.wcs_shape[1]
284284
self._n_pix = self.pix_ra * self.pix_dec
285+
dbgrank = MPI.COMM_WORLD.rank
285286
self._n_pix_submap = self._n_pix // self.submaps
286287
if self._n_pix_submap * self.submaps < self._n_pix:
287288
self._n_pix_submap += 1
@@ -317,6 +318,19 @@ def _exec(self, data, detectors=None, **kwargs):
317318
lonmax = max(lonmax, lnmax)
318319
latmin = min(latmin, ltmin)
319320
latmax = max(latmax, ltmax)
321+
if ob.comm.comm_group_rank is not None:
322+
# Synchronize between groups
323+
if ob.comm.group_rank == 0:
324+
lonmin = ob.comm.comm_group_rank.allreduce(lonmin, op=MPI.MIN)
325+
latmin = ob.comm.comm_group_rank.allreduce(latmin, op=MPI.MIN)
326+
lonmax = ob.comm.comm_group_rank.allreduce(lonmax, op=MPI.MAX)
327+
latmax = ob.comm.comm_group_rank.allreduce(latmax, op=MPI.MAX)
328+
# Broadcast result within the group
329+
if ob.comm.comm_group is not None:
330+
lonmin = ob.comm.comm_group.bcast(lonmin, root=0)
331+
lonmax = ob.comm.comm_group.bcast(lonmax, root=0)
332+
latmin = ob.comm.comm_group.bcast(latmin, root=0)
333+
latmax = ob.comm.comm_group.bcast(latmax, root=0)
320334
new_bounds = (
321335
(lonmax.to(u.degree), latmin.to(u.degree)),
322336
(lonmin.to(u.degree), latmax.to(u.degree)),
@@ -494,8 +508,8 @@ def _get_scan_range(self, obs):
494508
lon.append(phi)
495509
lat.append(np.pi / 2 - theta)
496510
else:
497-
lon.append(phi - center_lonlat[:, 0])
498-
lat.append((np.pi / 2 - theta) - center_lonlat[:, 1])
511+
lon.append(phi - center_lonlat[rank::ntask, 0])
512+
lat.append((np.pi / 2 - theta) - center_lonlat[rank::ntask, 1])
499513
lon = np.unwrap(np.hstack(lon))
500514
lat = np.hstack(lat)
501515

src/toast/pixels.py

+27
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ def alltoallv_info(self):
316316
- The locations in the receive buffer of each submap.
317317
318318
"""
319+
log = Logger.get()
319320
if self._alltoallv_info is not None:
320321
# Already computed
321322
return self._alltoallv_info
@@ -390,6 +391,17 @@ def alltoallv_info(self):
390391
recv_locations,
391392
)
392393

394+
msg_rank = 0
395+
if self._comm is not None:
396+
msg_rank = self._comm.rank
397+
msg = f"alltoallv_info[{msg_rank}]:\n"
398+
msg += f" send_counts={send_counts} "
399+
msg += f"send_displ={send_displ}\n"
400+
msg += f" recv_counts={recv_counts} "
401+
msg += f"recv_displ={recv_displ} "
402+
msg += f"recv_locations={recv_locations}"
403+
log.verbose(msg)
404+
393405
return self._alltoallv_info
394406

395407

@@ -744,6 +756,17 @@ def setup_alltoallv(self):
744756
for sm, locs in recv_locations.items():
745757
self._recv_locations[sm] = scale * np.array(locs, dtype=np.int32)
746758

759+
msg_rank = 0
760+
if self._dist.comm is not None:
761+
msg_rank = self._dist.comm.rank
762+
msg = f"setup_alltoallv[{msg_rank}]:\n"
763+
msg += f" send_counts={self._send_counts} "
764+
msg += f"send_displ={self._send_displ}\n"
765+
msg += f" recv_counts={self._recv_counts} "
766+
msg += f"recv_displ={self._recv_displ} "
767+
msg += f"recv_locations={self._recv_locations}"
768+
log.verbose(msg)
769+
747770
# Allocate a persistent single-submap buffer
748771
self._reduce_buf_raw = self.storage_class.zeros(self._n_submap_value)
749772
self.reduce_buf = self._reduce_buf_raw.array()
@@ -774,6 +797,9 @@ def setup_alltoallv(self):
774797
buf_check_fail = True
775798

776799
# Allocate a persistent receive buffer
800+
msg = f"{msg_rank}: allocate receive buffer of "
801+
msg += f"{recv_buf_size} elements"
802+
log.verbose(msg)
777803
self._receive_raw = self.storage_class.zeros(recv_buf_size)
778804
self.receive = self._receive_raw.array()
779805
except:
@@ -796,6 +822,7 @@ def forward_alltoallv(self):
796822
None.
797823
798824
"""
825+
log = Logger.get()
799826
gt = GlobalTimers.get()
800827
self.setup_alltoallv()
801828

src/toast/tests/_helpers.py

+46
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,52 @@ def create_space_telescope(group_size, sample_rate=10.0 * u.Hz, pixel_per_proces
100100
site = SpaceSite("L2")
101101
return Telescope("test", focalplane=fp, site=site)
102102

103+
def create_boresight_telescope(group_size, sample_rate=10.0 * u.Hz):
104+
"""Create a fake telescope with one boresight detector per process."""
105+
nullquat = np.array([0, 0, 0, 1], dtype=np.float64)
106+
n_det = group_size
107+
det_names = [f"d{x:03d}" for x in range(n_det)]
108+
pol_ang = np.array([(2*np.pi*x/n_det) for x in range(n_det)])
109+
110+
det_table = QTable(
111+
[
112+
Column(name="name", data=det_names),
113+
Column(name="quat", data=[nullquat for x in range(n_det)]),
114+
Column(name="pol_leakage", length=n_det, unit=None),
115+
Column(name="psi_pol", data=pol_ang, unit=u.rad),
116+
Column(name="fwhm", length=n_det, unit=u.arcmin),
117+
Column(name="psd_fmin", length=n_det, unit=u.Hz),
118+
Column(name="psd_fknee", length=n_det, unit=u.Hz),
119+
Column(name="psd_alpha", length=n_det, unit=None),
120+
Column(name="psd_net", length=n_det, unit=(u.K * np.sqrt(1.0 * u.second))),
121+
Column(name="bandcenter", length=n_det, unit=u.GHz),
122+
Column(name="bandwidth", length=n_det, unit=u.GHz),
123+
Column(
124+
name="pixel", data=[0 for x in range(n_det)]
125+
),
126+
]
127+
)
128+
129+
fwhm = 5.0 * u.arcmin
130+
131+
for idet in range(len(det_table)):
132+
det_table[idet]["pol_leakage"] = 0.0
133+
det_table[idet]["fwhm"] = fwhm
134+
det_table[idet]["bandcenter"] = 150.0 * u.GHz
135+
det_table[idet]["bandwidth"] = 20.0 * u.GHz
136+
det_table[idet]["psd_fmin"] = 1.0e-5 * u.Hz
137+
det_table[idet]["psd_fknee"] = 0.05 * u.Hz
138+
det_table[idet]["psd_alpha"] = 1.0
139+
det_table[idet]["psd_net"] = 100 * (u.K * np.sqrt(1.0 * u.second))
140+
141+
fp = Focalplane(
142+
detector_data=det_table,
143+
sample_rate=sample_rate,
144+
field_of_view=1.1 * (2 * fwhm),
145+
)
146+
147+
site = SpaceSite("L2")
148+
return Telescope("test", focalplane=fp, site=site)
103149

104150
def create_ground_telescope(
105151
group_size, sample_rate=10.0 * u.Hz, pixel_per_process=1, fknee=None

0 commit comments

Comments
 (0)