|
1 | 1 | import numpy as np |
2 | 2 |
|
3 | | -__all__ = ['legendre_transform', 'legendre_roots'] |
4 | | - |
5 | | -cdef extern from "sharp.h": |
6 | | - ctypedef long ptrdiff_t |
7 | | - |
8 | | - void sharp_legendre_transform_s(float *bl, float *recfac, ptrdiff_t lmax, float *x, |
9 | | - float *out, ptrdiff_t nx) |
10 | | - void sharp_legendre_transform(double *bl, double *recfac, ptrdiff_t lmax, double *x, |
11 | | - double *out, ptrdiff_t nx) |
12 | | - void sharp_legendre_transform_recfac(double *r, ptrdiff_t lmax) |
13 | | - void sharp_legendre_transform_recfac_s(float *r, ptrdiff_t lmax) |
14 | | - void sharp_legendre_roots(int n, double *x, double *w) |
| 3 | +__all__ = ['legendre_transform', 'legendre_roots', 'sht', 'synthesis', 'adjoint_synthesis', |
| 4 | + 'analysis', 'adjoint_analysis', 'healpix_grid', 'triangular_order', 'rectangular_order', |
| 5 | + 'packed_real_order'] |
15 | 6 |
|
16 | 7 |
|
17 | 8 | def legendre_transform(x, bl, out=None): |
@@ -54,3 +45,198 @@ def legendre_roots(n): |
54 | 45 | if n > 0: |
55 | 46 | sharp_legendre_roots(n, &x_buf[0], &w_buf[0]) |
56 | 47 | return x, w |
| 48 | + |
| 49 | + |
| 50 | +JOBTYPE_TO_CONST = { |
| 51 | + 'Y': SHARP_Y, |
| 52 | + 'Yt': SHARP_Yt, |
| 53 | + 'WY': SHARP_WY, |
| 54 | + 'YtW': SHARP_YtW |
| 55 | +} |
| 56 | + |
| 57 | + |
| 58 | +def sht(jobtype, geom_info ginfo, alm_info ainfo, double[::1] input, |
| 59 | + int spin=0, comm=None, add=False): |
| 60 | + cdef void *comm_ptr |
| 61 | + cdef int flags = SHARP_DP | (SHARP_ADD if add else 0) |
| 62 | + cdef double *palm |
| 63 | + cdef double *pmap |
| 64 | + cdef int r |
| 65 | + cdef sharp_jobtype jobtype_i |
| 66 | + cdef double[::1] output_buf |
| 67 | + |
| 68 | + try: |
| 69 | + jobtype_i = JOBTYPE_TO_CONST[jobtype] |
| 70 | + except KeyError: |
| 71 | + raise ValueError('jobtype must be one of: %s' % ', '.join(sorted(JOBTYPE_TO_CONST.keys()))) |
| 72 | + |
| 73 | + if jobtype_i == SHARP_Y or jobtype_i == SHARP_WY: |
| 74 | + output = np.empty(ginfo.local_size(), dtype=np.float64) |
| 75 | + output_buf = output |
| 76 | + pmap = &output_buf[0] |
| 77 | + palm = &input[0] |
| 78 | + else: |
| 79 | + output = np.empty(ainfo.local_size(), dtype=np.float64) |
| 80 | + output_buf = output |
| 81 | + pmap = &input[0] |
| 82 | + palm = &output_buf[0] |
| 83 | + |
| 84 | + if comm is None: |
| 85 | + with nogil: |
| 86 | + sharp_execute ( |
| 87 | + jobtype_i, |
| 88 | + geom_info=ginfo.ginfo, alm_info=ainfo.ainfo, |
| 89 | + spin=spin, alm=&palm, map=&pmap, |
| 90 | + ntrans=1, flags=flags, time=NULL, opcnt=NULL) |
| 91 | + else: |
| 92 | + from mpi4py import MPI |
| 93 | + if not isinstance(comm, MPI.Comm): |
| 94 | + raise TypeError('comm must be an mpi4py communicator') |
| 95 | + comm_ptr = <void*><size_t>MPI._addressof(comm) |
| 96 | + with nogil: |
| 97 | + r = sharp_execute_mpi_maybe ( |
| 98 | + comm_ptr, jobtype_i, |
| 99 | + geom_info=ginfo.ginfo, alm_info=ainfo.ainfo, |
| 100 | + spin=spin, alm=&palm, map=&pmap, |
| 101 | + ntrans=1, flags=flags, time=NULL, opcnt=NULL) |
| 102 | + if r == SHARP_ERROR_NO_MPI: |
| 103 | + raise Exception('MPI requested, but not available') |
| 104 | + |
| 105 | + return output |
| 106 | + |
| 107 | + |
| 108 | +def synthesis(*args, **kw): |
| 109 | + return sht('Y', *args, **kw) |
| 110 | + |
| 111 | +def adjoint_synthesis(*args, **kw): |
| 112 | + return sht('Yt', *args, **kw) |
| 113 | + |
| 114 | +def analysis(*args, **kw): |
| 115 | + return sht('YtW', *args, **kw) |
| 116 | + |
| 117 | +def adjoint_analysis(*args, **kw): |
| 118 | + return sht('WY', *args, **kw) |
| 119 | + |
| 120 | + |
| 121 | +# |
| 122 | +# geom_info |
| 123 | +# |
| 124 | +class NotInitializedError(Exception): |
| 125 | + pass |
| 126 | + |
| 127 | + |
| 128 | +cdef class geom_info: |
| 129 | + cdef sharp_geom_info *ginfo |
| 130 | + |
| 131 | + def __cinit__(self, *args, **kw): |
| 132 | + self.ginfo = NULL |
| 133 | + |
| 134 | + def local_size(self): |
| 135 | + if self.ginfo == NULL: |
| 136 | + raise NotInitializedError() |
| 137 | + return sharp_map_size(self.ginfo) |
| 138 | + |
| 139 | + def __dealloc__(self): |
| 140 | + if self.ginfo != NULL: |
| 141 | + sharp_destroy_geom_info(self.ginfo) |
| 142 | + self.ginfo = NULL |
| 143 | + |
| 144 | + |
| 145 | +cdef class healpix_grid(geom_info): |
| 146 | + |
| 147 | + _weight_cache = {} # { (nside, 'T'/'Q'/'U') -> numpy array of ring weights cached from file } |
| 148 | + |
| 149 | + def __init__(self, int nside, stride=1, int[::1] rings=None, double[::1] weights=None): |
| 150 | + if weights is not None and weights.shape[0] != 2 * nside: |
| 151 | + raise ValueError('weights must have length 2 * nside') |
| 152 | + sharp_make_subset_healpix_geom_info(nside, stride, |
| 153 | + nrings=4 * nside - 1 if rings is None else rings.shape[0], |
| 154 | + rings=NULL if rings is None else &rings[0], |
| 155 | + weight=NULL if weights is None else &weights[0], |
| 156 | + geom_info=&self.ginfo) |
| 157 | + |
| 158 | + @classmethod |
| 159 | + def load_ring_weights(cls, nside, fields): |
| 160 | + """ |
| 161 | + Loads HEALPix ring weights from file. The environment variable |
| 162 | + HEALPIX should be set, and this routine will look in the `data` |
| 163 | + subdirectory. |
| 164 | +
|
| 165 | + Parameters |
| 166 | + ---------- |
| 167 | +
|
| 168 | + nside: int |
| 169 | + HEALPix nside parameter |
| 170 | +
|
| 171 | + fields: tuple of str |
| 172 | + Which weights to extract; pass ('T',) to only get scalar |
| 173 | + weights back, or ('T', 'Q', 'U') to get all the weights |
| 174 | +
|
| 175 | + Returns |
| 176 | + ------- |
| 177 | +
|
| 178 | + List of NumPy arrays, according to fields parameter. |
| 179 | +
|
| 180 | + """ |
| 181 | + import os |
| 182 | + from astropy.io import fits |
| 183 | + data_path = os.path.join(os.environ['HEALPIX'], 'data') |
| 184 | + fits_field_names = { |
| 185 | + 'T': 'TEMPERATURE WEIGHTS', |
| 186 | + 'Q': 'Q-POLARISATION WEIGHTS', |
| 187 | + 'U': 'U-POLARISATION WEIGHTS'} |
| 188 | + |
| 189 | + must_load = [field for field in fields if (nside, field) not in cls._weight_cache] |
| 190 | + |
| 191 | + if must_load: |
| 192 | + hdulist = fits.open(os.path.join(data_path, 'weight_ring_n%05d.fits' % nside)) |
| 193 | + try: |
| 194 | + for field in must_load: |
| 195 | + w = hdulist[1].data.field(fits_field_names[field]).ravel().astype(np.double) |
| 196 | + w += 1 |
| 197 | + cls._weight_cache[nside, field] = w |
| 198 | + finally: |
| 199 | + hdulist.close() |
| 200 | + return [cls._weight_cache[(nside, field)].copy() for field in fields] |
| 201 | + |
| 202 | +# |
| 203 | +# alm_info |
| 204 | +# |
| 205 | + |
| 206 | + |
| 207 | +cdef class alm_info: |
| 208 | + cdef sharp_alm_info *ainfo |
| 209 | + |
| 210 | + def __cinit__(self, *args, **kw): |
| 211 | + self.ainfo = NULL |
| 212 | + |
| 213 | + def local_size(self): |
| 214 | + if self.ainfo == NULL: |
| 215 | + raise NotInitializedError() |
| 216 | + return sharp_alm_count(self.ainfo) |
| 217 | + |
| 218 | + def __dealloc__(self): |
| 219 | + if self.ainfo != NULL: |
| 220 | + sharp_destroy_alm_info(self.ainfo) |
| 221 | + self.ainfo = NULL |
| 222 | + |
| 223 | + |
| 224 | +cdef class triangular_order(alm_info): |
| 225 | + def __init__(self, int lmax, mmax=None, stride=1): |
| 226 | + mmax = mmax if mmax is not None else lmax |
| 227 | + sharp_make_triangular_alm_info(lmax, mmax, stride, &self.ainfo) |
| 228 | + |
| 229 | + |
| 230 | +cdef class rectangular_order(alm_info): |
| 231 | + def __init__(self, int lmax, mmax=None, stride=1): |
| 232 | + mmax = mmax if mmax is not None else lmax |
| 233 | + sharp_make_rectangular_alm_info(lmax, mmax, stride, &self.ainfo) |
| 234 | + |
| 235 | + |
| 236 | +cdef class packed_real_order(alm_info): |
| 237 | + def __init__(self, int lmax, stride=1, int[::1] ms=None): |
| 238 | + sharp_make_mmajor_real_packed_alm_info(lmax=lmax, stride=stride, |
| 239 | + nm=lmax + 1 if ms is None else ms.shape[0], |
| 240 | + ms=NULL if ms is None else &ms[0], |
| 241 | + alm_info=&self.ainfo) |
| 242 | + |
0 commit comments