Skip to content

Commit d0cd6b6

Browse files
authored
Add basic support for WCS pixelization. (#567)
* Add basic support for WCS pixelization. * Add a new PixelsWCS operator * Add support for source-centered projection * Add ScanWCSMap and ScanWCSMask operators * Add I/O routines * Add unit tests for use of projections in mapmaking Currently this work adds a dependency on pixell, which is used only to build the WCS parameters. This is something we could implement internally if needed. For example, if we want to support more architectures than are currently supported by pixell binary wheels. * Several small fixes needed due to rebase * Manually write wcs maps with astropy, to avoid pixell incompatibility. * Disable write of solver products by default * Restructure unit tests. Compute global coverage on the sky when autoscaling. Several unrelated small fixes. * Remove debug statement * Run format source and fix parallel read function * Decrease map resolution and increase condition number cut in unit tests. * Change another condition number threshold in the unit tests to try and make apple lapack happy. * Change another condition number threshold in the unit tests. * See if linalg on macos with python3.8 is better behaved. * Disable macos tests on python < 3.9
1 parent 6a0188e commit d0cd6b6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2299
-126
lines changed

.github/workflows/test.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ jobs:
7171
fail-fast: false
7272
matrix:
7373
include:
74-
- python: "3.7"
75-
pyshort: "37"
74+
# - python: "3.8"
75+
# pyshort: "38"
7676
- python: "3.9"
7777
pyshort: "39"
7878
# - python: "3.10"

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ def readme():
275275
"pyyaml",
276276
"astropy",
277277
"healpy",
278+
"pixell",
278279
"ephem",
279280
]
280281
conf["extras_require"] = {

src/toast/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ install(FILES
110110
config.py
111111
job.py
112112
pixels.py
113-
pixels_io.py
113+
pixels_io_healpix.py
114+
pixels_io_wcs.py
114115
covariance.py
115116
dist.py
116117
data.py

src/toast/_libtoast/ops_mapmaker_utils.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ void init_ops_mapmaker_utils(py::module & m) {
265265
use_det_flags
266266
);
267267
for (int64_t iw = 0; iw < nnz; iw++) {
268-
#pragma omp atomic
268+
# pragma omp atomic
269269
raw_zmap[zoff + iw] += zmap_val[iw];
270270
}
271271
}

src/toast/_libtoast/ops_pixels_healpix.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
// So we must duplicate this across compilation units.
2828

2929
void pixels_healpix_qa_rotate(double const * q_in, double const * v_in,
30-
double * v_out) {
30+
double * v_out) {
3131
// The input quaternion has already been normalized on the host.
3232

3333
double xw = q_in[3] * q_in[0];

src/toast/_libtoast/ops_stokes_weights.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
// So we must duplicate this across compilation units.
2020

2121
void stokes_weights_qa_rotate(double const * q_in, double const * v_in,
22-
double * v_out) {
22+
double * v_out) {
2323
// The input quaternion has already been normalized on the host.
2424

2525
double xw = q_in[3] * q_in[0];

src/toast/_libtoast/template_offset.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ void init_template_offset(py::module & m) {
201201
} else {
202202
contrib = raw_det_data[d];
203203
}
204-
#pragma omp atomic
204+
# pragma omp atomic
205205
raw_amplitudes[amp] += contrib;
206206
}
207207
offset += raw_n_amp_views[iview];

src/toast/dipole.py

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# All rights reserved. Use of this source code is governed by
33
# a BSD-style license that can be found in the LICENSE file.
44

5-
import healpy as hp
65
import numpy as np
76
import scipy.constants as constants
87
from astropy import units as u

src/toast/observation.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,13 @@ def __init__(
185185
self._uid = name_UID(self._name)
186186

187187
if self._session is None:
188-
self._session = Session(
189-
name=self._name,
190-
uid=self._uid,
191-
start=None,
192-
end=None,
193-
)
188+
if self._name is not None:
189+
self._session = Session(
190+
name=self._name,
191+
uid=self._uid,
192+
start=None,
193+
end=None,
194+
)
194195
elif not isinstance(self._session, Session):
195196
raise RuntimeError("session should be a Session instance or None")
196197

src/toast/ops/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ install(FILES
2727
pointing_detector.py
2828
scan_map.py
2929
scan_healpix.py
30+
scan_wcs.py
3031
mapmaker_utils.py
3132
mapmaker_binning.py
3233
mapmaker_solve.py
@@ -53,6 +54,7 @@ install(FILES
5354
demodulation.py
5455
stokes_weights.py
5556
pixels_healpix.py
57+
pixels_wcs.py
5658
filterbin.py
5759
save_hdf5.py
5860
load_hdf5.py

src/toast/ops/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from .operator import Operator
3737
from .pipeline import Pipeline
3838
from .pixels_healpix import PixelsHealpix
39+
from .pixels_wcs import PixelsWCS
3940
from .pointing import BuildPixelDistribution
4041
from .pointing_detector import PointingDetectorSimple
4142
from .polyfilter import CommonModeFilter, PolyFilter, PolyFilter2D
@@ -45,6 +46,7 @@
4546
from .save_spt3g import SaveSpt3g
4647
from .scan_healpix import ScanHealpixMap, ScanHealpixMask
4748
from .scan_map import ScanMap, ScanMask, ScanScale
49+
from .scan_wcs import ScanWCSMap, ScanWCSMask
4850
from .sim_cosmic_rays import InjectCosmicRays
4951
from .sim_crosstalk import CrossTalk, MitigateCrossTalk
5052
from .sim_gaindrifts import GainDrifter

src/toast/ops/crosslinking.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ..mpi import MPI
1313
from ..observation import default_values as defaults
1414
from ..pixels import PixelData, PixelDistribution
15-
from ..pixels_io import write_healpix_fits
15+
from ..pixels_io_healpix import write_healpix_fits
1616
from ..timing import Timer, function_timer
1717
from ..traits import Bool, Float, Instance, Int, Unicode, trait_docs
1818
from ..utils import Logger

src/toast/ops/filterbin.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from ..mpi import MPI
2323
from ..observation import default_values as defaults
2424
from ..pixels import PixelData, PixelDistribution
25-
from ..pixels_io import (
25+
from ..pixels_io_healpix import (
2626
filename_is_fits,
2727
filename_is_hdf5,
2828
read_healpix_fits,

src/toast/ops/groundfilter.py

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from time import time
66

7-
import healpy as hp
87
import numpy as np
98
import traitlets
109
from astropy import units as u

src/toast/ops/madam_utils.py

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import os
66

7-
import healpy as hp
87
import numpy as np
98

109
from ..timing import function_timer

src/toast/ops/mapmaker.py

+35-23
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from ..mpi import MPI
1111
from ..observation import default_values as defaults
1212
from ..pixels import PixelData, PixelDistribution
13-
from ..pixels_io import write_healpix_fits, write_healpix_hdf5
13+
from ..pixels_io_healpix import write_healpix_fits, write_healpix_hdf5
14+
from ..pixels_io_wcs import write_wcs_fits
1415
from ..timing import Timer, function_timer
1516
from ..traits import Bool, Float, Instance, Int, Unicode, trait_docs
1617
from ..utils import Logger
@@ -145,7 +146,7 @@ class MapMaker(Operator):
145146
write_rcond = Bool(True, help="If True, write the reciprocal condition numbers.")
146147

147148
write_solver_products = Bool(
148-
True, help="If True, write out equivalent solver products."
149+
False, help="If True, write out equivalent solver products."
149150
)
150151

151152
keep_solver_products = Bool(
@@ -423,9 +424,14 @@ def _exec(self, data, detectors=None, **kwargs):
423424
]
424425
).apply(data)
425426

426-
# FIXME: This all assumes the pointing operator is an instance of the
427-
# PointingHealpix class. We need to generalize distributed pixel data
428-
# formats and associate them with the pointing operator.
427+
# FIXME: This I/O technique assumes "known" types of pixel representations.
428+
# Instead, we should associate read / write functions to a particular pixel
429+
# class.
430+
431+
is_pix_wcs = hasattr(map_binning.pixel_pointing, "wcs")
432+
is_hpix_nest = None
433+
if not is_pix_wcs:
434+
is_hpix_nest = map_binning.pixel_pointing.nest
429435

430436
write_del = list()
431437
write_del.append((self.hits_name, self.write_hits))
@@ -439,25 +445,31 @@ def _exec(self, data, detectors=None, **kwargs):
439445
wtimer.start()
440446
for prod_key, prod_write in write_del:
441447
if prod_write:
442-
if self.write_hdf5:
443-
# Non-standard HDF5 output
444-
fname = os.path.join(self.output_dir, "{}.h5".format(prod_key))
445-
write_healpix_hdf5(
446-
data[prod_key],
447-
fname,
448-
nest=map_binning.pixel_pointing.nest,
449-
single_precision=True,
450-
force_serial=self.write_hdf5_serial,
451-
)
452-
else:
453-
# Standard FITS output
448+
if is_pix_wcs:
454449
fname = os.path.join(self.output_dir, "{}.fits".format(prod_key))
455-
write_healpix_fits(
456-
data[prod_key],
457-
fname,
458-
nest=map_binning.pixel_pointing.nest,
459-
report_memory=self.report_memory,
460-
)
450+
write_wcs_fits(data[prod_key], fname)
451+
else:
452+
if self.write_hdf5:
453+
# Non-standard HDF5 output
454+
fname = os.path.join(self.output_dir, "{}.h5".format(prod_key))
455+
write_healpix_hdf5(
456+
data[prod_key],
457+
fname,
458+
nest=is_hpix_nest,
459+
single_precision=True,
460+
force_serial=self.write_hdf5_serial,
461+
)
462+
else:
463+
# Standard FITS output
464+
fname = os.path.join(
465+
self.output_dir, "{}.fits".format(prod_key)
466+
)
467+
write_healpix_fits(
468+
data[prod_key],
469+
fname,
470+
nest=is_hpix_nest,
471+
report_memory=self.report_memory,
472+
)
461473
log.info_rank(f"Wrote {fname} in", comm=comm, timer=wtimer)
462474
if not self.keep_final_products:
463475
if prod_key in data:

src/toast/ops/mapmaker_templates.py

+35-19
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
from ..mpi import MPI
1212
from ..observation import default_values as defaults
1313
from ..pixels import PixelData, PixelDistribution
14-
from ..pixels_io import write_healpix_fits, write_healpix_hdf5
14+
from ..pixels_io_healpix import write_healpix_fits, write_healpix_hdf5
15+
from ..pixels_io_wcs import write_wcs_fits
1516
from ..templates import AmplitudesMap, Template
1617
from ..timing import Timer, function_timer
1718
from ..traits import Bool, Float, Instance, Int, List, Unicode, trait_docs
@@ -785,6 +786,15 @@ def _exec(self, data, detectors=None, **kwargs):
785786
memreport.prefix = "After solving for amplitudes"
786787
memreport.apply(data)
787788

789+
# FIXME: This I/O technique assumes "known" types of pixel representations.
790+
# Instead, we should associate read / write functions to a particular pixel
791+
# class.
792+
793+
is_pix_wcs = hasattr(self.binning.pixel_pointing, "wcs")
794+
is_hpix_nest = None
795+
if not is_pix_wcs:
796+
is_hpix_nest = self.binning.pixel_pointing.nest
797+
788798
write_del = [
789799
self.solver_hits_name,
790800
self.solver_cov_name,
@@ -794,25 +804,31 @@ def _exec(self, data, detectors=None, **kwargs):
794804
]
795805
for prod_key in write_del:
796806
if self.write_solver_products:
797-
if self.write_hdf5:
798-
# Non-standard HDF5 output
799-
fname = os.path.join(self.output_dir, "{}.h5".format(prod_key))
800-
write_healpix_hdf5(
801-
data[prod_key],
802-
fname,
803-
nest=self.binning.pixel_pointing.nest,
804-
single_precision=True,
805-
force_serial=self.write_hdf5_serial,
806-
)
807-
else:
808-
# Standard FITS output
807+
if is_pix_wcs:
809808
fname = os.path.join(self.output_dir, "{}.fits".format(prod_key))
810-
write_healpix_fits(
811-
data[prod_key],
812-
fname,
813-
nest=self.binning.pixel_pointing.nest,
814-
report_memory=self.report_memory,
815-
)
809+
write_wcs_fits(data[prod_key], fname)
810+
else:
811+
if self.write_hdf5:
812+
# Non-standard HDF5 output
813+
fname = os.path.join(self.output_dir, "{}.h5".format(prod_key))
814+
write_healpix_hdf5(
815+
data[prod_key],
816+
fname,
817+
nest=is_hpix_nest,
818+
single_precision=True,
819+
force_serial=self.write_hdf5_serial,
820+
)
821+
else:
822+
# Standard FITS output
823+
fname = os.path.join(
824+
self.output_dir, "{}.fits".format(prod_key)
825+
)
826+
write_healpix_fits(
827+
data[prod_key],
828+
fname,
829+
nest=is_hpix_nest,
830+
report_memory=self.report_memory,
831+
)
816832
if not self.mc_mode and not self.keep_solver_products:
817833
if prod_key in data:
818834
data[prod_key].clear()

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)

0 commit comments

Comments
 (0)