| [2131108] | 1 | /*
|
|---|
| 2 | * Copyright (c) 2010-2013 Centre National de la Recherche Scientifique.
|
|---|
| 3 | * written by Nathanael Schaeffer (CNRS, ISTerre, Grenoble, France).
|
|---|
| 4 | *
|
|---|
| 5 | * nathanael.schaeffer@ujf-grenoble.fr
|
|---|
| 6 | *
|
|---|
| 7 | * This software is governed by the CeCILL license under French law and
|
|---|
| 8 | * abiding by the rules of distribution of free software. You can use,
|
|---|
| 9 | * modify and/or redistribute the software under the terms of the CeCILL
|
|---|
| 10 | * license as circulated by CEA, CNRS and INRIA at the following URL
|
|---|
| 11 | * "http://www.cecill.info".
|
|---|
| 12 | *
|
|---|
| 13 | * The fact that you are presently reading this means that you have had
|
|---|
| 14 | * knowledge of the CeCILL license and that you accept its terms.
|
|---|
| 15 | *
|
|---|
| 16 | */
|
|---|
| 17 |
|
|---|
| 18 | /* shtns_numpy.i : SWIG interface to Python using NumPy */
|
|---|
| 19 |
|
|---|
| 20 | /* TODO and known problems :
|
|---|
| 21 | * - alignement on 16 bytes of NumPy arrays is not guaranteed. It should however work on 64bit systems or on modern 32bit systems.
|
|---|
| 22 | * - you may have to adjust the path below to include the header file "arrayobject.h" from the NumPy package.
|
|---|
| 23 | */
|
|---|
| 24 |
|
|---|
| 25 | %module (docstring="Python/NumPy interface to the SHTns spherical harmonic transform library") shtns
|
|---|
| 26 |
|
|---|
| 27 | %init{
|
|---|
| 28 | import_array(); // required by NumPy
|
|---|
| 29 | }
|
|---|
| 30 |
|
|---|
| 31 | %pythoncode{
|
|---|
| 32 | import numpy as np
|
|---|
| 33 | }
|
|---|
| 34 |
|
|---|
| 35 | %{
|
|---|
| 36 |
|
|---|
| 37 | #include <numpy/arrayobject.h>
|
|---|
| 38 | #include "sht_private.h"
|
|---|
| 39 |
|
|---|
| 40 | // variables used for exception handling.
|
|---|
| 41 | static int shtns_error = 0;
|
|---|
| 42 | static char* shtns_err_msg;
|
|---|
| 43 | static char msg_buffer[128];
|
|---|
| 44 | static char msg_grid_err[] = "Grid not set. Call .set_grid() mehtod.";
|
|---|
| 45 | static char msg_numpy_arr[] = "Numpy array expected.";
|
|---|
| 46 | static char msg_rot_err[] = "truncation must be triangular (lmax=mmax, mres=1)";
|
|---|
| 47 |
|
|---|
| 48 | static void throw_exception(int error, int iarg, char* msg)
|
|---|
| 49 | {
|
|---|
| 50 | shtns_error = error;
|
|---|
| 51 | shtns_err_msg = msg;
|
|---|
| 52 | if (iarg > 0) {
|
|---|
| 53 | sprintf(msg_buffer, "arg #%d : %.100s", iarg, msg);
|
|---|
| 54 | shtns_err_msg = msg_buffer;
|
|---|
| 55 | }
|
|---|
| 56 | }
|
|---|
| 57 |
|
|---|
| 58 | static int check_spatial(int i, PyObject *a, int size) {
|
|---|
| 59 | if (size == 0) {
|
|---|
| 60 | throw_exception(SWIG_RuntimeError,0,msg_grid_err); return 0;
|
|---|
| 61 | }
|
|---|
| 62 | if (!PyArray_Check(a)) {
|
|---|
| 63 | throw_exception(SWIG_TypeError,i,msg_numpy_arr); return 0;
|
|---|
| 64 | }
|
|---|
| 65 | if (PyArray_TYPE(a) != PyArray_DOUBLE) {
|
|---|
| 66 | throw_exception(SWIG_TypeError,i,"spatial array must consist of float."); return 0;
|
|---|
| 67 | }
|
|---|
| 68 | if (!PyArray_ISCONTIGUOUS(a)) {
|
|---|
| 69 | throw_exception(SWIG_RuntimeError,i,"spatial array not contiguous. Use 'b=a.copy()' to copy a to a contiguous array b."); return 0;
|
|---|
| 70 | }
|
|---|
| 71 | if (PyArray_SIZE(a) != size) {
|
|---|
| 72 | throw_exception(SWIG_RuntimeError,i,"spatial array has wrong size"); return 0;
|
|---|
| 73 | }
|
|---|
| 74 | return 1;
|
|---|
| 75 | }
|
|---|
| 76 |
|
|---|
| 77 | static int check_spectral(int i, PyObject *a, int size) {
|
|---|
| 78 | if (!PyArray_Check(a)) {
|
|---|
| 79 | throw_exception(SWIG_RuntimeError,i,msg_numpy_arr); return 0;
|
|---|
| 80 | }
|
|---|
| 81 | if (PyArray_TYPE(a) != PyArray_CDOUBLE) {
|
|---|
| 82 | throw_exception(SWIG_RuntimeError,i,"spectral array must consist of complex float. Create with: 'sh.spec_array()'"); return 0;
|
|---|
| 83 | }
|
|---|
| 84 | if (!PyArray_ISCONTIGUOUS(a)) {
|
|---|
| 85 | throw_exception(SWIG_RuntimeError,i,"spactral array not contiguous. Use .copy() to copy to a contiguous array."); return 0;
|
|---|
| 86 | }
|
|---|
| 87 | if (PyArray_SIZE(a) != size) {
|
|---|
| 88 | throw_exception(SWIG_RuntimeError,i,"spectral array has wrong size"); return 0;
|
|---|
| 89 | }
|
|---|
| 90 | return 1;
|
|---|
| 91 | }
|
|---|
| 92 |
|
|---|
| 93 | inline PyObject* SpecArray_New(int size) {
|
|---|
| 94 | npy_intp dims = size;
|
|---|
| 95 | npy_intp strides = sizeof(cplx);
|
|---|
| 96 | return PyArray_New(&PyArray_Type, 1, &dims, PyArray_CDOUBLE, &strides, NULL, strides, 0, NULL);
|
|---|
| 97 | }
|
|---|
| 98 |
|
|---|
| 99 | inline PyObject* SpatArray_New(int size) {
|
|---|
| 100 | npy_intp dims = size;
|
|---|
| 101 | npy_intp strides = sizeof(double);
|
|---|
| 102 | return PyArray_New(&PyArray_Type, 1, &dims, PyArray_DOUBLE, &strides, NULL, strides, 0, NULL);
|
|---|
| 103 | }
|
|---|
| 104 |
|
|---|
| 105 | %}
|
|---|
| 106 |
|
|---|
| 107 | // main object is renamed to sht.
|
|---|
| 108 | %rename("sht") shtns_info;
|
|---|
| 109 | %ignore SHT_NATIVE_LAYOUT;
|
|---|
| 110 | %ignore nlat_2;
|
|---|
| 111 | %ignore lmidx;
|
|---|
| 112 | %ignore li;
|
|---|
| 113 | %ignore mi;
|
|---|
| 114 | %ignore ct;
|
|---|
| 115 | %ignore st;
|
|---|
| 116 |
|
|---|
| 117 | %rename(print_version) shtns_print_version;
|
|---|
| 118 | %rename(set_verbosity) shtns_verbose;
|
|---|
| 119 |
|
|---|
| 120 | %feature("autodoc");
|
|---|
| 121 | %include "shtns.h"
|
|---|
| 122 | %include "exception.i"
|
|---|
| 123 |
|
|---|
| 124 |
|
|---|
| 125 | %extend shtns_info {
|
|---|
| 126 | %exception {
|
|---|
| 127 | shtns_error = 0; // clear exception
|
|---|
| 128 | $function
|
|---|
| 129 | if (shtns_error) { // test for exception
|
|---|
| 130 | SWIG_exception(shtns_error, shtns_err_msg); return NULL;
|
|---|
| 131 | }
|
|---|
| 132 | }
|
|---|
| 133 |
|
|---|
| 134 | %pythonappend shtns_info %{
|
|---|
| 135 | ## array giving the degree of spherical harmonic coefficients.
|
|---|
| 136 | self.l = np.zeros(self.nlm, dtype=np.int32)
|
|---|
| 137 | ## array giving the order of spherical harmonic coefficients.
|
|---|
| 138 | self.m = np.zeros(self.nlm, dtype=np.int32)
|
|---|
| 139 | for mloop in range(0, self.mmax*self.mres+1, self.mres):
|
|---|
| 140 | for lloop in range(mloop, self.lmax+1):
|
|---|
| 141 | ii = self.idx(lloop,mloop)
|
|---|
| 142 | self.m[ii] = mloop
|
|---|
| 143 | self.l[ii] = lloop
|
|---|
| 144 | self.m.flags.writeable = False # prevent writing in m and l arrays
|
|---|
| 145 | self.l.flags.writeable = False
|
|---|
| 146 | %}
|
|---|
| 147 | %feature("kwargs") shtns_info;
|
|---|
| 148 | shtns_info(int lmax, int mmax=-1, int mres=1, int norm=sht_orthonormal, int nthreads=0) { // default arguments : mmax, mres and norm
|
|---|
| 149 | if (lmax < 2) {
|
|---|
| 150 | throw_exception(SWIG_ValueError,1,"lmax < 2 not allowed"); return NULL;
|
|---|
| 151 | }
|
|---|
| 152 | if (mres <= 0) {
|
|---|
| 153 | throw_exception(SWIG_ValueError,3,"mres <= 0 invalid"); return NULL;
|
|---|
| 154 | }
|
|---|
| 155 | if (mmax < 0) mmax = lmax/mres; // default mmax
|
|---|
| 156 | if (mmax*mres > lmax) {
|
|---|
| 157 | throw_exception(SWIG_ValueError,1,"lmax < mmax*mres invalid"); return NULL;
|
|---|
| 158 | }
|
|---|
| 159 | shtns_use_threads(nthreads); // use nthreads openmp threads if available (0 means auto)
|
|---|
| 160 | return shtns_create(lmax, mmax, mres, norm);
|
|---|
| 161 | }
|
|---|
| 162 |
|
|---|
| 163 | ~shtns_info() {
|
|---|
| 164 | shtns_destroy($self); // free memory.
|
|---|
| 165 | }
|
|---|
| 166 |
|
|---|
| 167 | %pythonappend set_grid %{
|
|---|
| 168 | ## array giving the cosine of the colatitude for the grid.
|
|---|
| 169 | self.cos_theta = self.__ct()
|
|---|
| 170 | self.cos_theta.flags.writeable = False
|
|---|
| 171 | ## shape of a spatial array for the grid (tuple of 2 values).
|
|---|
| 172 | self.spat_shape = tuple(self.__spat_shape())
|
|---|
| 173 | %}
|
|---|
| 174 | %apply int *OUTPUT { int *nlat_out };
|
|---|
| 175 | %apply int *OUTPUT { int *nphi_out };
|
|---|
| 176 | %feature("kwargs") set_grid;
|
|---|
| 177 | void set_grid(int nlat=0, int nphi=0, int flags=sht_quick_init, double polar_opt=1.0e-8, int nl_order=1, int *nlat_out, int *nphi_out) { // default arguments
|
|---|
| 178 | if (nlat != 0) {
|
|---|
| 179 | if (nlat <= $self->lmax) { // nlat too small
|
|---|
| 180 | throw_exception(SWIG_ValueError,1,"nlat <= lmax"); return;
|
|---|
| 181 | }
|
|---|
| 182 | if (nlat & 1) { // nlat must be even
|
|---|
| 183 | throw_exception(SWIG_ValueError,1,"nlat must be even"); return;
|
|---|
| 184 | }
|
|---|
| 185 | }
|
|---|
| 186 | if ((nphi != 0) && (nphi <= $self->mmax *2)) { // nphi too small
|
|---|
| 187 | throw_exception(SWIG_ValueError,2,"nphi <= 2*mmax"); return;
|
|---|
| 188 | }
|
|---|
| 189 | if (!(flags & SHT_THETA_CONTIGUOUS)) flags |= SHT_PHI_CONTIGUOUS; // default to SHT_PHI_CONTIGUOUS.
|
|---|
| 190 | *nlat_out = nlat; *nphi_out = nphi;
|
|---|
| 191 | shtns_set_grid_auto($self, flags, polar_opt, nl_order, nlat_out, nphi_out);
|
|---|
| 192 | }
|
|---|
| 193 |
|
|---|
| 194 | void print_info() {
|
|---|
| 195 | shtns_print_cfg($self);
|
|---|
| 196 | }
|
|---|
| 197 | double sh00_1() {
|
|---|
| 198 | return sh00_1($self);
|
|---|
| 199 | }
|
|---|
| 200 | double sh10_ct() {
|
|---|
| 201 | return sh10_ct($self);
|
|---|
| 202 | }
|
|---|
| 203 | double sh11_st() {
|
|---|
| 204 | return sh11_st($self);
|
|---|
| 205 | }
|
|---|
| 206 | double shlm_e1(unsigned l, unsigned m) {
|
|---|
| 207 | return shlm_e1($self, l, m);
|
|---|
| 208 | }
|
|---|
| 209 |
|
|---|
| 210 | /* returns useful data */
|
|---|
| 211 | PyObject* __ct() { // grid must have been initialized.
|
|---|
| 212 | PyObject *obj;
|
|---|
| 213 | double *ct;
|
|---|
| 214 | int i;
|
|---|
| 215 | if ($self->nlat == 0) { // no grid
|
|---|
| 216 | throw_exception(SWIG_RuntimeError,0,msg_grid_err);
|
|---|
| 217 | return NULL;
|
|---|
| 218 | }
|
|---|
| 219 | obj = SpatArray_New($self->nlat);
|
|---|
| 220 | ct = (double*) PyArray_DATA(obj);
|
|---|
| 221 | for (i=0; i<$self->nlat; i++) ct[i] = $self->ct[i]; // copy
|
|---|
| 222 | return obj;
|
|---|
| 223 | }
|
|---|
| 224 | PyObject* gauss_wts() { // gauss grid must have been initialized.
|
|---|
| 225 | PyObject *obj;
|
|---|
| 226 | if ($self->nlat == 0) { // no grid
|
|---|
| 227 | throw_exception(SWIG_RuntimeError,0,msg_grid_err);
|
|---|
| 228 | return NULL;
|
|---|
| 229 | }
|
|---|
| 230 | if ($self->wg == NULL) {
|
|---|
| 231 | throw_exception(SWIG_RuntimeError,0,"not a gauss grid");
|
|---|
| 232 | return NULL;
|
|---|
| 233 | }
|
|---|
| 234 | obj = SpatArray_New($self->nlat_2);
|
|---|
| 235 | shtns_gauss_wts($self, PyArray_DATA(obj));
|
|---|
| 236 | return obj;
|
|---|
| 237 | }
|
|---|
| 238 | PyObject* mul_ct_matrix() {
|
|---|
| 239 | PyObject *mx = SpatArray_New(2*$self->nlm);
|
|---|
| 240 | mul_ct_matrix($self, PyArray_DATA(mx));
|
|---|
| 241 | return mx;
|
|---|
| 242 | }
|
|---|
| 243 | PyObject* st_dt_matrix() {
|
|---|
| 244 | PyObject *mx = SpatArray_New(2*$self->nlm);
|
|---|
| 245 | st_dt_matrix($self, PyArray_DATA(mx));
|
|---|
| 246 | return mx;
|
|---|
| 247 | }
|
|---|
| 248 |
|
|---|
| 249 | %apply int *OUTPUT { int *dim0 };
|
|---|
| 250 | %apply int *OUTPUT { int *dim1 };
|
|---|
| 251 | void __spat_shape(int *dim0, int *dim1) {
|
|---|
| 252 | *dim0 = $self->nphi; *dim1 = $self->nlat;
|
|---|
| 253 | if ($self->fftc_mode == 1) { // phi-contiguous
|
|---|
| 254 | *dim0 = $self->nlat; *dim1 = $self->nphi;
|
|---|
| 255 | }
|
|---|
| 256 | }
|
|---|
| 257 |
|
|---|
| 258 | %pythoncode %{
|
|---|
| 259 | def spec_array(self, im=-1):
|
|---|
| 260 | """return a numpy array of spherical harmonic coefficients (complex). Adress coefficients with index sh.idx(l,m)
|
|---|
| 261 | if optional argument im is given, the spectral array is restricted to order im*mres."""
|
|---|
| 262 | if im<0:
|
|---|
| 263 | return np.zeros(self.nlm, dtype=complex)
|
|---|
| 264 | else:
|
|---|
| 265 | return np.zeros(self.lmax + 1 - im*self.mres, dtype=complex)
|
|---|
| 266 |
|
|---|
| 267 | def spat_array(self):
|
|---|
| 268 | """return a numpy array of 2D spatial field."""
|
|---|
| 269 | if self.nlat == 0: raise RuntimeError("Grid not set. Call .set_grid() mehtod.")
|
|---|
| 270 | return np.zeros(self.spat_shape)
|
|---|
| 271 | %}
|
|---|
| 272 |
|
|---|
| 273 | // returns the index in a spectral array of (l,m) coefficient.
|
|---|
| 274 | int idx(unsigned l, unsigned m) {
|
|---|
| 275 | if (l > $self->lmax) {
|
|---|
| 276 | throw_exception(SWIG_ValueError,1,"l invalid"); return 0;
|
|---|
| 277 | }
|
|---|
| 278 | if ( (m > l) || (m > $self->mmax * $self->mres) || (m % $self->mres != 0) ) {
|
|---|
| 279 | throw_exception(SWIG_ValueError,2,"m invalid"); return 0;
|
|---|
| 280 | }
|
|---|
| 281 | return LM($self, l, m);
|
|---|
| 282 | }
|
|---|
| 283 |
|
|---|
| 284 | /* scalar transforms */
|
|---|
| 285 | void spat_to_SH(PyObject *Vr, PyObject *Qlm) {
|
|---|
| 286 | if (check_spatial(1,Vr, $self->nspat) && check_spectral(2,Qlm, $self->nlm))
|
|---|
| 287 | spat_to_SH($self, PyArray_DATA(Vr), PyArray_DATA(Qlm));
|
|---|
| 288 | }
|
|---|
| 289 | void SH_to_spat(PyObject *Qlm, PyObject *Vr) {
|
|---|
| 290 | if (check_spatial(2,Vr, $self->nspat) && check_spectral(1,Qlm, $self->nlm))
|
|---|
| 291 | SH_to_spat($self, PyArray_DATA(Qlm), PyArray_DATA(Vr));
|
|---|
| 292 | }
|
|---|
| 293 | /* complex transforms */
|
|---|
| 294 | void spat_cplx_to_SH(PyObject *z, PyObject *alm) {
|
|---|
| 295 | int n = $self->lmax + 1;
|
|---|
| 296 | if (check_spectral(1,z, $self->nspat) && check_spectral(2,alm, n*n))
|
|---|
| 297 | spat_cplx_to_SH($self, PyArray_DATA(z), PyArray_DATA(alm));
|
|---|
| 298 | }
|
|---|
| 299 | void SH_to_spat_cplx(PyObject *alm, PyObject *z) {
|
|---|
| 300 | int n = $self->lmax + 1;
|
|---|
| 301 | if (check_spectral(2,z, $self->nspat) && check_spectral(1,alm, n*n))
|
|---|
| 302 | SH_to_spat_cplx($self, PyArray_DATA(alm), PyArray_DATA(z));
|
|---|
| 303 | }
|
|---|
| 304 | /*void SH_to_spat_grad(PyObject *alm, PyObject *gt, PyObject *gp) {
|
|---|
| 305 | if (check_spatial(3,gp, $self->nspat) && check_spatial(2,gt, $self->nspat) && check_spectral(1,alm, $self->nlm))
|
|---|
| 306 | SH_to_spat_grad($self, PyArray_DATA(alm), PyArray_DATA(gt), PyArray_DATA(gp));
|
|---|
| 307 | }*/
|
|---|
| 308 |
|
|---|
| 309 | /* 2D vectors */
|
|---|
| 310 | void spat_to_SHsphtor(PyObject *Vt, PyObject *Vp, PyObject *Slm, PyObject *Tlm) {
|
|---|
| 311 | if (check_spatial(1,Vt, $self->nspat) && check_spatial(2,Vp, $self->nspat) && check_spectral(3,Slm, $self->nlm) && check_spectral(4,Tlm, $self->nlm))
|
|---|
| 312 | spat_to_SHsphtor($self, PyArray_DATA(Vt), PyArray_DATA(Vp), PyArray_DATA(Slm), PyArray_DATA(Tlm));
|
|---|
| 313 | }
|
|---|
| 314 | void SHsphtor_to_spat(PyObject *Slm, PyObject *Tlm, PyObject *Vt, PyObject *Vp) {
|
|---|
| 315 | if (check_spatial(3,Vt, $self->nspat) && check_spatial(4,Vp, $self->nspat) && check_spectral(1,Slm, $self->nlm) && check_spectral(2,Tlm, $self->nlm))
|
|---|
| 316 | SHsphtor_to_spat($self, PyArray_DATA(Slm), PyArray_DATA(Tlm), PyArray_DATA(Vt), PyArray_DATA(Vp));
|
|---|
| 317 | }
|
|---|
| 318 | void SHsph_to_spat(PyObject *Slm, PyObject *Vt, PyObject *Vp) {
|
|---|
| 319 | if (check_spatial(2,Vt, $self->nspat) && check_spatial(3,Vp, $self->nspat) && check_spectral(1,Slm, $self->nlm))
|
|---|
| 320 | SHsph_to_spat($self, PyArray_DATA(Slm), PyArray_DATA(Vt), PyArray_DATA(Vp));
|
|---|
| 321 | }
|
|---|
| 322 | void SHtor_to_spat(PyObject *Tlm, PyObject *Vt, PyObject *Vp) {
|
|---|
| 323 | if (check_spatial(2,Vt, $self->nspat) && check_spatial(3,Vp, $self->nspat) && check_spectral(1,Tlm, $self->nlm))
|
|---|
| 324 | SHtor_to_spat($self, PyArray_DATA(Tlm), PyArray_DATA(Vt), PyArray_DATA(Vp));
|
|---|
| 325 | }
|
|---|
| 326 |
|
|---|
| 327 | /* 3D vectors */
|
|---|
| 328 | void spat_to_SHqst(PyObject *Vr, PyObject *Vt, PyObject *Vp, PyObject *Qlm, PyObject *Slm, PyObject *Tlm) {
|
|---|
| 329 | if (check_spatial(1,Vr, $self->nspat) && check_spatial(2,Vt, $self->nspat) && check_spatial(3,Vp, $self->nspat)
|
|---|
| 330 | && check_spectral(4,Qlm, $self->nlm) && check_spectral(5,Slm, $self->nlm) && check_spectral(6,Tlm, $self->nlm))
|
|---|
| 331 | spat_to_SHqst($self, PyArray_DATA(Vr), PyArray_DATA(Vt), PyArray_DATA(Vp), PyArray_DATA(Qlm), PyArray_DATA(Slm), PyArray_DATA(Tlm));
|
|---|
| 332 | }
|
|---|
| 333 | void SHqst_to_spat(PyObject *Qlm, PyObject *Slm, PyObject *Tlm, PyObject *Vr, PyObject *Vt, PyObject *Vp) {
|
|---|
| 334 | if (check_spatial(4,Vr, $self->nspat) && check_spatial(5,Vt, $self->nspat) && check_spatial(6,Vp, $self->nspat)
|
|---|
| 335 | && check_spectral(1,Qlm, $self->nlm) && check_spectral(2,Slm, $self->nlm) && check_spectral(3,Tlm, $self->nlm))
|
|---|
| 336 | SHqst_to_spat($self, PyArray_DATA(Qlm), PyArray_DATA(Slm), PyArray_DATA(Tlm), PyArray_DATA(Vr), PyArray_DATA(Vt), PyArray_DATA(Vp));
|
|---|
| 337 | }
|
|---|
| 338 |
|
|---|
| 339 | %pythoncode %{
|
|---|
| 340 | def synth(self,*arg):
|
|---|
| 341 | """
|
|---|
| 342 | spectral to spatial transform, for scalar or vector data.
|
|---|
| 343 | v = synth(qlm) : compute the spatial representation of the scalar qlm
|
|---|
| 344 | vtheta,vphi = synth(slm,tlm) : compute the 2D spatial vector from its spectral spheroidal/toroidal scalars (slm,tlm)
|
|---|
| 345 | vr,vtheta,vphi = synth(qlm,slm,tlm) : compute the 3D spatial vector from its spectral radial/spheroidal/toroidal scalars (qlm,slm,tlm)
|
|---|
| 346 | """
|
|---|
| 347 | if self.nlat == 0: raise RuntimeError("Grid not set. Call .set_grid() mehtod.")
|
|---|
| 348 | n = len(arg)
|
|---|
| 349 | if (n>3) or (n<1): raise RuntimeError("1,2 or 3 arguments required.")
|
|---|
| 350 | q = list(arg)
|
|---|
| 351 | for i in range(0,n):
|
|---|
| 352 | if q[i].size != self.nlm: raise RuntimeError("spectral array has wrong size.")
|
|---|
| 353 | if q[i].dtype.num != np.dtype('complex128').num: raise RuntimeError("spectral array should be dtype=complex.")
|
|---|
| 354 | if q[i].flags.contiguous == False: q[i] = q[i].copy() # contiguous array required.
|
|---|
| 355 | if n==1: #scalar transform
|
|---|
| 356 | vr = np.empty(self.spat_shape)
|
|---|
| 357 | self.SH_to_spat(q[0],vr)
|
|---|
| 358 | return vr
|
|---|
| 359 | elif n==2: # 2D vector transform
|
|---|
| 360 | vt = np.empty(self.spat_shape) # v_theta
|
|---|
| 361 | vp = np.empty(self.spat_shape) # v_phi
|
|---|
| 362 | self.SHsphtor_to_spat(q[0],q[1],vt,vp)
|
|---|
| 363 | return vt,vp
|
|---|
| 364 | else: # 3D vector transform
|
|---|
| 365 | vr = np.empty(self.spat_shape) # v_r
|
|---|
| 366 | vt = np.empty(self.spat_shape) # v_theta
|
|---|
| 367 | vp = np.empty(self.spat_shape) # v_phi
|
|---|
| 368 | self.SHqst_to_spat(q[0],q[1],q[2],vr,vt,vp)
|
|---|
| 369 | return vr,vt,vp
|
|---|
| 370 |
|
|---|
| 371 | def analys(self,*arg):
|
|---|
| 372 | """
|
|---|
| 373 | spatial to spectral transform, for scalar or vector data.
|
|---|
| 374 | qlm = analys(q) : compute the spherical harmonic representation of the scalar q
|
|---|
| 375 | slm,tlm = analys(vtheta,vphi) : compute the spectral spheroidal/toroidal scalars (slm,tlm) from 2D vector components (vtheta, vphi)
|
|---|
| 376 | qlm,slm,tlm = synth(vr,vtheta,vphi) : compute the spectral radial/spheroidal/toroidal scalars (qlm,slm,tlm) from 3D vector components (vr,vtheta,vphi)
|
|---|
| 377 | """
|
|---|
| 378 | if self.nlat == 0: raise RuntimeError("Grid not set. Call .set_grid() mehtod.")
|
|---|
| 379 | if abs(self.cos_theta[0]) == 1: raise RuntimeError("Analysis not allowed with sht_reg_poles grid.")
|
|---|
| 380 | n = len(arg)
|
|---|
| 381 | if (n>3) or (n<1): raise RuntimeError("1,2 or 3 arguments required.")
|
|---|
| 382 | v = list(arg)
|
|---|
| 383 | for i in range(0,n):
|
|---|
| 384 | if v[i].shape != self.spat_shape: raise RuntimeError("spatial array has wrong shape.")
|
|---|
| 385 | if v[i].dtype.num != np.dtype('float64').num: raise RuntimeError("spatial array should be dtype=float64.")
|
|---|
| 386 | if v[i].flags.contiguous == False: v[i] = v[i].copy() # contiguous array required.
|
|---|
| 387 | if n==1:
|
|---|
| 388 | q = np.empty(self.nlm, dtype=complex)
|
|---|
| 389 | self.spat_to_SH(v[0],q)
|
|---|
| 390 | return q
|
|---|
| 391 | elif n==2:
|
|---|
| 392 | s = np.empty(self.nlm, dtype=complex)
|
|---|
| 393 | t = np.empty(self.nlm, dtype=complex)
|
|---|
| 394 | self.spat_to_SHsphtor(v[0],v[1],s,t)
|
|---|
| 395 | return s,t
|
|---|
| 396 | else:
|
|---|
| 397 | q = np.empty(self.nlm, dtype=complex)
|
|---|
| 398 | s = np.empty(self.nlm, dtype=complex)
|
|---|
| 399 | t = np.empty(self.nlm, dtype=complex)
|
|---|
| 400 | self.spat_to_SHqst(v[0],v[1],v[2],q,s,t)
|
|---|
| 401 | return q,s,t
|
|---|
| 402 |
|
|---|
| 403 | def synth_grad(self,slm):
|
|---|
| 404 | """(vtheta,vphi) = synth_grad(sht self, slm) : compute the spatial representation of the gradient of slm"""
|
|---|
| 405 | if self.nlat == 0: raise RuntimeError("Grid not set. Call .set_grid() mehtod.")
|
|---|
| 406 | if slm.size != self.nlm: raise RuntimeError("spectral array has wrong size.")
|
|---|
| 407 | if slm.dtype.num != np.dtype('complex128').num: raise RuntimeError("spectral array should be dtype=complex.")
|
|---|
| 408 | if slm.flags.contiguous == False: slm = slm.copy() # contiguous array required.
|
|---|
| 409 | vt = np.empty(self.spat_shape)
|
|---|
| 410 | vp = np.empty(self.spat_shape)
|
|---|
| 411 | self.SHsph_to_spat(slm,vt,vp)
|
|---|
| 412 | return vt,vp
|
|---|
| 413 |
|
|---|
| 414 | def synth_cplx(self,alm):
|
|---|
| 415 | """
|
|---|
| 416 | spectral to spatial transform, for complex valued scalar data.
|
|---|
| 417 | z = synth(alm) : compute the spatial representation of the scalar alm
|
|---|
| 418 | """
|
|---|
| 419 | if self.nlat == 0: raise RuntimeError("Grid not set. Call .set_grid() mehtod.")
|
|---|
| 420 | if self.lmax != self.mmax: raise RuntimeError("complex SH requires lmax=mmax and mres=1.")
|
|---|
| 421 | if alm.size != (self.lmax+1)**2: raise RuntimeError("spectral array has wrong size.")
|
|---|
| 422 | if alm.dtype.num != np.dtype('complex128').num: raise RuntimeError("spectral array should be dtype=complex.")
|
|---|
| 423 | if alm.flags.contiguous == False: alm = alm.copy() # contiguous array required.
|
|---|
| 424 | z = np.empty(self.spat_shape, dtype=complex)
|
|---|
| 425 | self.SH_to_spat_cplx(alm,z)
|
|---|
| 426 | return z
|
|---|
| 427 |
|
|---|
| 428 | def analys_cplx(self,z):
|
|---|
| 429 | """
|
|---|
| 430 | spatial to spectral transform, for complex valued scalar data.
|
|---|
| 431 | alm = analys(z) : compute the spherical harmonic representation of the complex scalar z
|
|---|
| 432 | """
|
|---|
| 433 | if self.nlat == 0: raise RuntimeError("Grid not set. Call .set_grid() mehtod.")
|
|---|
| 434 | if self.lmax != self.mmax: raise RuntimeError("complex SH requires lmax=mmax and mres=1.")
|
|---|
| 435 | if z.shape != self.spat_shape: raise RuntimeError("spatial array has wrong shape.")
|
|---|
| 436 | if z.dtype.num != np.dtype('complex128').num: raise RuntimeError("spatial array should be dtype=complex128.")
|
|---|
| 437 | if z.flags.contiguous == False: z = z.copy() # contiguous array required.
|
|---|
| 438 | alm = np.empty((self.lmax+1)**2, dtype=complex)
|
|---|
| 439 | self.spat_cplx_to_SH(z,alm)
|
|---|
| 440 | return alm
|
|---|
| 441 |
|
|---|
| 442 | def zidx(self, l,m):
|
|---|
| 443 | """
|
|---|
| 444 | zidx(sht self, int l, int m) -> int : compute the index l*(l+1)+m in a complex spherical harmonic expansion
|
|---|
| 445 | """
|
|---|
| 446 | l = np.asarray(l)
|
|---|
| 447 | m = np.asarray(m)
|
|---|
| 448 | if (l>self.lmax).any() or (abs(m)>l).any() : raise RuntimeError("invalid range for l,m")
|
|---|
| 449 | return l*(l+1)+m
|
|---|
| 450 |
|
|---|
| 451 | def zlm(self, idx):
|
|---|
| 452 | """
|
|---|
| 453 | zlm(sht self, int idx) -> (int,int) : returns the l and m corresponding to the given index in complex spherical harmonic expansion
|
|---|
| 454 | """
|
|---|
| 455 | idx = np.asarray(idx)
|
|---|
| 456 | if (idx >= (self.lmax+1)**2).any() or (idx < 0).any() : raise RuntimeError("invalid range for l,m")
|
|---|
| 457 | l = np.sqrt(idx).astype(int)
|
|---|
| 458 | m = idx - l*(l+1)
|
|---|
| 459 | return l,m
|
|---|
| 460 |
|
|---|
| 461 | def spec_array_cplx(self):
|
|---|
| 462 | """return a numpy array that can hold the spectral representation of a complex scalar spatial field."""
|
|---|
| 463 | return np.zeros((self.lmax+1)**2, dtype=complex)
|
|---|
| 464 |
|
|---|
| 465 | def spat_array_cplx(self):
|
|---|
| 466 | """return a numpy array of 2D complex spatial field."""
|
|---|
| 467 | if self.nlat == 0: raise RuntimeError("Grid not set. Call .set_grid() mehtod.")
|
|---|
| 468 | return np.zeros(self.spat_shape, dtype=complex128)
|
|---|
| 469 | %}
|
|---|
| 470 |
|
|---|
| 471 | /* local evaluations */
|
|---|
| 472 | double SH_to_point(PyObject *Qlm, double cost, double phi) {
|
|---|
| 473 | if (check_spectral(1,Qlm, $self->nlm)) return SH_to_point($self, PyArray_DATA(Qlm), cost, phi);
|
|---|
| 474 | return 0.0;
|
|---|
| 475 | }
|
|---|
| 476 | %apply double *OUTPUT { double *vr };
|
|---|
| 477 | %apply double *OUTPUT { double *vt };
|
|---|
| 478 | %apply double *OUTPUT { double *vp };
|
|---|
| 479 | void SH_to_grad_point(PyObject *DrSlm, PyObject *Slm, double cost, double phi, double *vr, double *vt, double *vp) {
|
|---|
| 480 | if (check_spectral(1,DrSlm, $self->nlm) && check_spectral(2,Slm, $self->nlm))
|
|---|
| 481 | SH_to_grad_point($self, PyArray_DATA(DrSlm), PyArray_DATA(Slm), cost, phi, vr, vt, vp);
|
|---|
| 482 | }
|
|---|
| 483 | void SHqst_to_point(PyObject *Qlm, PyObject *Slm, PyObject *Tlm,
|
|---|
| 484 | double cost, double phi, double *vr, double *vt, double *vp) {
|
|---|
| 485 | if (check_spectral(1,Qlm, $self->nlm) && check_spectral(2,Slm, $self->nlm) && check_spectral(3,Tlm, $self->nlm))
|
|---|
| 486 | SHqst_to_point($self, PyArray_DATA(Qlm), PyArray_DATA(Slm), PyArray_DATA(Tlm), cost, phi, vr, vt, vp);
|
|---|
| 487 | }
|
|---|
| 488 | %clear double *vr;
|
|---|
| 489 | %clear double *vt;
|
|---|
| 490 | %clear double *vp;
|
|---|
| 491 |
|
|---|
| 492 | /* rotation of SH representations (experimental) */
|
|---|
| 493 | PyObject* Zrotate(PyObject *Qlm, double alpha) {
|
|---|
| 494 | if (check_spectral(1,Qlm, $self->nlm)) {
|
|---|
| 495 | PyObject *Rlm = SpecArray_New($self->nlm);
|
|---|
| 496 | SH_Zrotate($self, PyArray_DATA(Qlm), alpha, PyArray_DATA(Rlm));
|
|---|
| 497 | return Rlm;
|
|---|
| 498 | }
|
|---|
| 499 | return NULL;
|
|---|
| 500 | }
|
|---|
| 501 | PyObject* Yrotate(PyObject *Qlm, double alpha) {
|
|---|
| 502 | if (($self->mres != 1)||($self->mmax != $self->lmax)) {
|
|---|
| 503 | throw_exception(SWIG_RuntimeError,0,msg_rot_err); return NULL;
|
|---|
| 504 | }
|
|---|
| 505 | if (check_spectral(1,Qlm, $self->nlm)) {
|
|---|
| 506 | PyObject *Rlm = SpecArray_New($self->nlm);
|
|---|
| 507 | SH_Yrotate($self, PyArray_DATA(Qlm), alpha, PyArray_DATA(Rlm));
|
|---|
| 508 | return Rlm;
|
|---|
| 509 | }
|
|---|
| 510 | return NULL;
|
|---|
| 511 | }
|
|---|
| 512 | PyObject* Yrotate90(PyObject *Qlm) {
|
|---|
| 513 | if (($self->mres != 1)||($self->mmax != $self->lmax)) {
|
|---|
| 514 | throw_exception(SWIG_RuntimeError,0,msg_rot_err); return NULL;
|
|---|
| 515 | }
|
|---|
| 516 | if (check_spectral(1,Qlm, $self->nlm)) {
|
|---|
| 517 | PyObject *Rlm = SpecArray_New($self->nlm);
|
|---|
| 518 | SH_Yrotate90($self, PyArray_DATA(Qlm), PyArray_DATA(Rlm));
|
|---|
| 519 | return Rlm;
|
|---|
| 520 | }
|
|---|
| 521 | return NULL;
|
|---|
| 522 | }
|
|---|
| 523 | PyObject* Xrotate90(PyObject *Qlm) {
|
|---|
| 524 | if (($self->mres != 1)||($self->mmax != $self->lmax)) {
|
|---|
| 525 | throw_exception(SWIG_RuntimeError,0,msg_rot_err); return NULL;
|
|---|
| 526 | }
|
|---|
| 527 | if (check_spectral(1,Qlm, $self->nlm)) {
|
|---|
| 528 | PyObject *Rlm = SpecArray_New($self->nlm);
|
|---|
| 529 | SH_Xrotate90($self, PyArray_DATA(Qlm), PyArray_DATA(Rlm));
|
|---|
| 530 | return Rlm;
|
|---|
| 531 | }
|
|---|
| 532 | return NULL;
|
|---|
| 533 | }
|
|---|
| 534 |
|
|---|
| 535 | /* multiplication by l+1 l-1 matrix (mul_ct_matrix or st_dt_matrix) */
|
|---|
| 536 | PyObject* SH_mul_mx(PyObject *mx, PyObject *Qlm) {
|
|---|
| 537 | if (check_spectral(2,Qlm, $self->nlm) && check_spatial(1, mx, 2* $self->nlm)) {
|
|---|
| 538 | PyObject *Rlm = SpecArray_New($self->nlm);
|
|---|
| 539 | SH_mul_mx($self, PyArray_DATA(mx), PyArray_DATA(Qlm), PyArray_DATA(Rlm));
|
|---|
| 540 | return Rlm;
|
|---|
| 541 | }
|
|---|
| 542 | return NULL;
|
|---|
| 543 | }
|
|---|
| 544 |
|
|---|
| 545 | /* Legendre transforms (no fft) at given order m */
|
|---|
| 546 | void spat_to_SH_m(PyObject *Vr, PyObject *Qlm, PyObject *im) {
|
|---|
| 547 | int im_ = PyLong_AsLong(im); int ltr = $self->lmax;
|
|---|
| 548 | if ((im_ >= 0) && check_spectral(1,Vr, $self->nlat) && check_spectral(2,Qlm, ltr+1 - im_*$self->mres))
|
|---|
| 549 | spat_to_SH_ml($self, im_, PyArray_DATA(Vr), PyArray_DATA(Qlm), ltr);
|
|---|
| 550 | }
|
|---|
| 551 | void SH_to_spat_m(PyObject *Qlm, PyObject *Vr, PyObject *im) {
|
|---|
| 552 | int im_ = PyLong_AsLong(im); int ltr = $self->lmax;
|
|---|
| 553 | if ((im_ >= 0) && check_spectral(2,Vr, $self->nlat) && check_spectral(1,Qlm, ltr+1 - im_*$self->mres))
|
|---|
| 554 | SH_to_spat_ml($self, im_, PyArray_DATA(Qlm), PyArray_DATA(Vr), ltr);
|
|---|
| 555 | }
|
|---|
| 556 | void spat_to_SHsphtor_m(PyObject *Vt, PyObject *Vp, PyObject *Slm, PyObject *Tlm, PyObject *im) {
|
|---|
| 557 | int im_ = PyLong_AsLong(im); int ltr = $self->lmax; int nelem = ltr+1 - im_*$self->mres;
|
|---|
| 558 | if ((im_ >= 0) && check_spectral(1,Vt, $self->nlat) && check_spectral(2,Vp, $self->nlat) && check_spectral(3,Slm, nelem) && check_spectral(4,Tlm, nelem))
|
|---|
| 559 | spat_to_SHsphtor_ml($self, im_, PyArray_DATA(Vt), PyArray_DATA(Vp), PyArray_DATA(Slm), PyArray_DATA(Tlm), ltr);
|
|---|
| 560 | }
|
|---|
| 561 | void SHsphtor_to_spat_m(PyObject *Slm, PyObject *Tlm, PyObject *Vt, PyObject *Vp, PyObject *im) {
|
|---|
| 562 | int im_ = PyLong_AsLong(im); int ltr = $self->lmax; int nelem = ltr+1 - im_*$self->mres;
|
|---|
| 563 | if ((im_ >= 0) && check_spectral(3,Vt, $self->nlat) && check_spectral(4,Vp, $self->nlat) && check_spectral(1,Slm, nelem) && check_spectral(2,Tlm, nelem))
|
|---|
| 564 | SHsphtor_to_spat_ml($self, im_, PyArray_DATA(Slm), PyArray_DATA(Tlm), PyArray_DATA(Vt), PyArray_DATA(Vp), ltr);
|
|---|
| 565 | }
|
|---|
| 566 | void SHsph_to_spat_m(PyObject *Slm, PyObject *Vt, PyObject *Vp, PyObject *im) {
|
|---|
| 567 | int im_ = PyLong_AsLong(im); int ltr = $self->lmax; int nelem = ltr+1 - im_*$self->mres;
|
|---|
| 568 | if ((im_ >= 0) && check_spectral(2,Vt, $self->nlat) && check_spectral(3,Vp, $self->nlat) && check_spectral(1,Slm, nelem))
|
|---|
| 569 | SHsph_to_spat_ml($self, im_, PyArray_DATA(Slm), PyArray_DATA(Vt), PyArray_DATA(Vp), ltr);
|
|---|
| 570 | }
|
|---|
| 571 | void SHtor_to_spat_m(PyObject *Tlm, PyObject *Vt, PyObject *Vp, PyObject *im) {
|
|---|
| 572 | int im_ = PyLong_AsLong(im); int ltr = $self->lmax; int nelem = ltr+1 - im_*$self->mres;
|
|---|
| 573 | if ((im_ >= 0) && check_spectral(2,Vt, $self->nlat) && check_spectral(3,Vp, $self->nlat) && check_spectral(1,Tlm, nelem))
|
|---|
| 574 | SHtor_to_spat_ml($self, im_, PyArray_DATA(Tlm), PyArray_DATA(Vt), PyArray_DATA(Vp), ltr);
|
|---|
| 575 | }
|
|---|
| 576 | void spat_to_SHqst_m(PyObject *Vr, PyObject *Vt, PyObject *Vp, PyObject *Qlm, PyObject *Slm, PyObject *Tlm, PyObject *im) {
|
|---|
| 577 | int im_ = PyLong_AsLong(im); int ltr = $self->lmax; int nelem = ltr+1 - im_*$self->mres;
|
|---|
| 578 | if ((im_ >= 0) && check_spectral(1,Vr, $self->nlat) && check_spectral(2,Vt, $self->nlat) && check_spectral(3,Vp, $self->nlat)
|
|---|
| 579 | && check_spectral(4,Qlm, nelem) && check_spectral(5,Slm, nelem) && check_spectral(6,Tlm, nelem))
|
|---|
| 580 | spat_to_SHqst_ml($self, im_, PyArray_DATA(Vr), PyArray_DATA(Vt), PyArray_DATA(Vp), PyArray_DATA(Qlm), PyArray_DATA(Slm), PyArray_DATA(Tlm), ltr);
|
|---|
| 581 | }
|
|---|
| 582 | void SHqst_to_spat_m(PyObject *Qlm, PyObject *Slm, PyObject *Tlm, PyObject *Vr, PyObject *Vt, PyObject *Vp, PyObject *im) {
|
|---|
| 583 | int im_ = PyLong_AsLong(im); int ltr = $self->lmax; int nelem = ltr+1 - im_*$self->mres;
|
|---|
| 584 | if ((im_ >= 0) && check_spectral(4,Vr, $self->nlat) && check_spectral(5,Vt, $self->nlat) && check_spectral(6,Vp, $self->nlat)
|
|---|
| 585 | && check_spectral(1,Qlm, nelem) && check_spectral(2,Slm, nelem) && check_spectral(3,Tlm, nelem))
|
|---|
| 586 | SHqst_to_spat_ml($self, im_, PyArray_DATA(Qlm), PyArray_DATA(Slm), PyArray_DATA(Tlm), PyArray_DATA(Vr), PyArray_DATA(Vt), PyArray_DATA(Vp), ltr);
|
|---|
| 587 | }
|
|---|
| 588 |
|
|---|
| 589 | };
|
|---|