1 /*
2  * Copyright (c) 2016-2021, The OSKAR Developers.
3  * See the LICENSE file at the top-level directory of this distribution.
4  */
5 
6 #include <Python.h>
7 
8 #include <oskar.h>
9 #include <oskar_version.h>
10 #include <string.h>
11 
12 /* http://docs.scipy.org/doc/numpy-dev/reference/c-api.deprecations.html */
13 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
14 #include <numpy/arrayobject.h>
15 
16 static const char module_doc[] =
17         "This module provides an interface to the OSKAR sky model.";
18 static const char name[] = "oskar_Sky";
19 
20 #define deg2rad 1.74532925199432957692369e-2
21 #define arcsec2rad 4.84813681109535993589914e-6
22 #ifndef M_PI
23 #define M_PI 3.14159265358979323846264338327950288
24 #endif
25 
get_handle(PyObject * capsule,const char * name)26 static void* get_handle(PyObject* capsule, const char* name)
27 {
28     void* h = 0;
29     if (!PyCapsule_CheckExact(capsule))
30     {
31         PyErr_SetString(PyExc_RuntimeError, "Object is not a PyCapsule.");
32         return 0;
33     }
34     if (!(h = PyCapsule_GetPointer(capsule, name)))
35     {
36         PyErr_Format(PyExc_RuntimeError, "Capsule is not of type %s.", name);
37         return 0;
38     }
39     return h;
40 }
41 
42 
sky_free(PyObject * capsule)43 static void sky_free(PyObject* capsule)
44 {
45     int status = 0;
46     oskar_sky_free((oskar_Sky*) get_handle(capsule, name), &status);
47 }
48 
49 
numpy_type_from_oskar(int type)50 static int numpy_type_from_oskar(int type)
51 {
52     switch (type)
53     {
54     case OSKAR_INT:            return NPY_INT;
55     case OSKAR_SINGLE:         return NPY_FLOAT;
56     case OSKAR_DOUBLE:         return NPY_DOUBLE;
57     case OSKAR_SINGLE_COMPLEX: return NPY_CFLOAT;
58     case OSKAR_DOUBLE_COMPLEX: return NPY_CDOUBLE;
59     }
60     return 0;
61 }
62 
63 
append(PyObject * self,PyObject * args)64 static PyObject* append(PyObject* self, PyObject* args)
65 {
66     oskar_Sky *h1 = 0, *h2 = 0;
67     PyObject *capsule1 = 0, *capsule2 = 0;
68     int status = 0;
69     if (!PyArg_ParseTuple(args, "OO", &capsule1, &capsule2)) return 0;
70     if (!(h1 = (oskar_Sky*) get_handle(capsule1, name))) return 0;
71     if (!(h2 = (oskar_Sky*) get_handle(capsule2, name))) return 0;
72 
73     /* Append the sky model. */
74     oskar_sky_append(h1, h2, &status);
75 
76     /* Check for errors. */
77     if (status)
78     {
79         PyErr_Format(PyExc_RuntimeError,
80                 "oskar_sky_append() failed with code %d (%s).",
81                 status, oskar_get_error_string(status));
82         return 0;
83     }
84     return Py_BuildValue("");
85 }
86 
87 
append_sources(PyObject * self,PyObject * args)88 static PyObject* append_sources(PyObject* self, PyObject* args)
89 {
90     oskar_Sky *h = 0;
91     PyObject *obj[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
92     oskar_Mem *ra_c, *dec_c, *I_c, *Q_c, *U_c, *V_c;
93     oskar_Mem *ref_c, *spix_c, *rm_c, *maj_c, *min_c, *pa_c;
94     PyArrayObject *ra = 0, *dec = 0, *I = 0, *Q = 0, *U = 0, *V = 0;
95     PyArrayObject *ref = 0, *spix = 0, *rm = 0, *maj = 0, *min = 0, *pa = 0;
96     int status = 0, npy_type, type, flags, num_sources, old_num;
97 
98     /* Parse inputs: RA, Dec, I, Q, U, V, ref, spix, rm, maj, min, pa. */
99     if (!PyArg_ParseTuple(args, "OOOOOOOOOOOOO", &obj[0],
100             &obj[1], &obj[2], &obj[3], &obj[4], &obj[5], &obj[6],
101             &obj[7], &obj[8], &obj[9], &obj[10], &obj[11], &obj[12]))
102         return 0;
103     if (!(h = (oskar_Sky*) get_handle(obj[0], name))) return 0;
104 
105     /* Make sure input objects are arrays. Convert if required. */
106     flags = NPY_ARRAY_FORCECAST | NPY_ARRAY_IN_ARRAY;
107     type = oskar_sky_precision(h);
108     npy_type = numpy_type_from_oskar(type);
109     ra   = (PyArrayObject*) PyArray_FROM_OTF(obj[1], npy_type, flags);
110     dec  = (PyArrayObject*) PyArray_FROM_OTF(obj[2], npy_type, flags);
111     I    = (PyArrayObject*) PyArray_FROM_OTF(obj[3], npy_type, flags);
112     Q    = (PyArrayObject*) PyArray_FROM_OTF(obj[4], npy_type, flags);
113     U    = (PyArrayObject*) PyArray_FROM_OTF(obj[5], npy_type, flags);
114     V    = (PyArrayObject*) PyArray_FROM_OTF(obj[6], npy_type, flags);
115     ref  = (PyArrayObject*) PyArray_FROM_OTF(obj[7], npy_type, flags);
116     spix = (PyArrayObject*) PyArray_FROM_OTF(obj[8], npy_type, flags);
117     rm   = (PyArrayObject*) PyArray_FROM_OTF(obj[9], npy_type, flags);
118     maj  = (PyArrayObject*) PyArray_FROM_OTF(obj[10], npy_type, flags);
119     min  = (PyArrayObject*) PyArray_FROM_OTF(obj[11], npy_type, flags);
120     pa   = (PyArrayObject*) PyArray_FROM_OTF(obj[12], npy_type, flags);
121     if (!ra || !dec || !I || !Q || !U || !V ||
122             !ref || !spix || !rm || !maj || !min || !pa)
123         goto fail;
124 
125     /* Check size of input arrays. */
126     num_sources = (int) PyArray_SIZE(I);
127     if (num_sources != (int) PyArray_SIZE(ra) ||
128             num_sources != (int) PyArray_SIZE(dec) ||
129             num_sources != (int) PyArray_SIZE(Q) ||
130             num_sources != (int) PyArray_SIZE(U) ||
131             num_sources != (int) PyArray_SIZE(V) ||
132             num_sources != (int) PyArray_SIZE(ref) ||
133             num_sources != (int) PyArray_SIZE(spix) ||
134             num_sources != (int) PyArray_SIZE(rm) ||
135             num_sources != (int) PyArray_SIZE(maj) ||
136             num_sources != (int) PyArray_SIZE(min) ||
137             num_sources != (int) PyArray_SIZE(pa))
138     {
139         PyErr_SetString(PyExc_RuntimeError, "Input data dimension mismatch.");
140         goto fail;
141     }
142 
143     /* Pointers to input arrays. */
144     ra_c = oskar_mem_create_alias_from_raw(PyArray_DATA(ra),
145             type, OSKAR_CPU, num_sources, &status);
146     dec_c = oskar_mem_create_alias_from_raw(PyArray_DATA(dec),
147             type, OSKAR_CPU, num_sources, &status);
148     I_c = oskar_mem_create_alias_from_raw(PyArray_DATA(I),
149             type, OSKAR_CPU, num_sources, &status);
150     Q_c = oskar_mem_create_alias_from_raw(PyArray_DATA(Q),
151             type, OSKAR_CPU, num_sources, &status);
152     U_c = oskar_mem_create_alias_from_raw(PyArray_DATA(U),
153             type, OSKAR_CPU, num_sources, &status);
154     V_c = oskar_mem_create_alias_from_raw(PyArray_DATA(V),
155             type, OSKAR_CPU, num_sources, &status);
156     ref_c = oskar_mem_create_alias_from_raw(PyArray_DATA(ref),
157             type, OSKAR_CPU, num_sources, &status);
158     spix_c = oskar_mem_create_alias_from_raw(PyArray_DATA(spix),
159             type, OSKAR_CPU, num_sources, &status);
160     rm_c = oskar_mem_create_alias_from_raw(PyArray_DATA(rm),
161             type, OSKAR_CPU, num_sources, &status);
162     maj_c = oskar_mem_create_alias_from_raw(PyArray_DATA(maj),
163             type, OSKAR_CPU, num_sources, &status);
164     min_c = oskar_mem_create_alias_from_raw(PyArray_DATA(min),
165             type, OSKAR_CPU, num_sources, &status);
166     pa_c = oskar_mem_create_alias_from_raw(PyArray_DATA(pa),
167             type, OSKAR_CPU, num_sources, &status);
168 
169     /* Copy source data into the sky model. */
170     old_num = oskar_sky_num_sources(h);
171     oskar_sky_resize(h, old_num + num_sources, &status);
172     oskar_mem_copy_contents(oskar_sky_ra_rad(h), ra_c,
173             old_num, 0, num_sources, &status);
174     oskar_mem_copy_contents(oskar_sky_dec_rad(h), dec_c,
175             old_num, 0, num_sources, &status);
176     oskar_mem_copy_contents(oskar_sky_I(h), I_c,
177             old_num, 0, num_sources, &status);
178     oskar_mem_copy_contents(oskar_sky_Q(h), Q_c,
179             old_num, 0, num_sources, &status);
180     oskar_mem_copy_contents(oskar_sky_U(h), U_c,
181             old_num, 0, num_sources, &status);
182     oskar_mem_copy_contents(oskar_sky_V(h), V_c,
183             old_num, 0, num_sources, &status);
184     oskar_mem_copy_contents(oskar_sky_reference_freq_hz(h), ref_c,
185             old_num, 0, num_sources, &status);
186     oskar_mem_copy_contents(oskar_sky_spectral_index(h), spix_c,
187             old_num, 0, num_sources, &status);
188     oskar_mem_copy_contents(oskar_sky_rotation_measure_rad(h), rm_c,
189             old_num, 0, num_sources, &status);
190     oskar_mem_copy_contents(oskar_sky_fwhm_major_rad(h), maj_c,
191             old_num, 0, num_sources, &status);
192     oskar_mem_copy_contents(oskar_sky_fwhm_minor_rad(h), min_c,
193             old_num, 0, num_sources, &status);
194     oskar_mem_copy_contents(oskar_sky_position_angle_rad(h), pa_c,
195             old_num, 0, num_sources, &status);
196 
197     /* Free memory. */
198     oskar_mem_free(ra_c, &status);
199     oskar_mem_free(dec_c, &status);
200     oskar_mem_free(I_c, &status);
201     oskar_mem_free(Q_c, &status);
202     oskar_mem_free(U_c, &status);
203     oskar_mem_free(V_c, &status);
204     oskar_mem_free(ref_c, &status);
205     oskar_mem_free(spix_c, &status);
206     oskar_mem_free(rm_c, &status);
207     oskar_mem_free(maj_c, &status);
208     oskar_mem_free(min_c, &status);
209     oskar_mem_free(pa_c, &status);
210 
211     /* Check for errors. */
212     if (status)
213     {
214         PyErr_Format(PyExc_RuntimeError,
215                 "Sky model append_sources() failed with code %d (%s).",
216                 status, oskar_get_error_string(status));
217         goto fail;
218     }
219 
220     Py_XDECREF(ra);
221     Py_XDECREF(dec);
222     Py_XDECREF(I);
223     Py_XDECREF(Q);
224     Py_XDECREF(U);
225     Py_XDECREF(V);
226     Py_XDECREF(ref);
227     Py_XDECREF(spix);
228     Py_XDECREF(rm);
229     Py_XDECREF(maj);
230     Py_XDECREF(min);
231     Py_XDECREF(pa);
232     return Py_BuildValue("");
233 
234 fail:
235     Py_XDECREF(ra);
236     Py_XDECREF(dec);
237     Py_XDECREF(I);
238     Py_XDECREF(Q);
239     Py_XDECREF(U);
240     Py_XDECREF(V);
241     Py_XDECREF(ref);
242     Py_XDECREF(spix);
243     Py_XDECREF(rm);
244     Py_XDECREF(maj);
245     Py_XDECREF(min);
246     Py_XDECREF(pa);
247     return 0;
248 }
249 
250 
append_file(PyObject * self,PyObject * args)251 static PyObject* append_file(PyObject* self, PyObject* args)
252 {
253     oskar_Sky *h = 0, *temp = 0;
254     PyObject* capsule = 0;
255     int status = 0;
256     const char* filename = 0;
257     if (!PyArg_ParseTuple(args, "Os", &capsule, &filename)) return 0;
258     if (!(h = (oskar_Sky*) get_handle(capsule, name))) return 0;
259 
260     /* Load the sky model. */
261     temp = oskar_sky_load(filename, oskar_sky_precision(h), &status);
262     oskar_sky_append(h, temp, &status);
263     oskar_sky_free(temp, &status);
264 
265     /* Check for errors. */
266     if (status)
267     {
268         PyErr_Format(PyExc_RuntimeError,
269                 "oskar_sky_load() failed with code %d (%s).",
270                 status, oskar_get_error_string(status));
271         return 0;
272     }
273     return Py_BuildValue("");
274 }
275 
276 
capsule_name(PyObject * self,PyObject * args)277 static PyObject* capsule_name(PyObject* self, PyObject* args)
278 {
279     PyObject *capsule = 0;
280     if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
281     if (!PyCapsule_CheckExact(capsule))
282     {
283         PyErr_SetString(PyExc_RuntimeError, "Object is not a PyCapsule.");
284         return 0;
285     }
286     return Py_BuildValue("s", PyCapsule_GetName(capsule));
287 }
288 
289 
create(PyObject * self,PyObject * args)290 static PyObject* create(PyObject* self, PyObject* args)
291 {
292     oskar_Sky* h = 0;
293     PyObject* capsule = 0;
294     int status = 0, prec = 0;
295     const char* type;
296     if (!PyArg_ParseTuple(args, "s", &type)) return 0;
297     prec = (type[0] == 'S' || type[0] == 's') ? OSKAR_SINGLE : OSKAR_DOUBLE;
298     h = oskar_sky_create(prec, OSKAR_CPU, 0, &status);
299     capsule = PyCapsule_New((void*)h, name, (PyCapsule_Destructor)sky_free);
300     return Py_BuildValue("N", capsule); /* Don't increment refcount. */
301 }
302 
303 
create_copy(PyObject * self,PyObject * args)304 static PyObject* create_copy(PyObject* self, PyObject* args)
305 {
306     oskar_Sky *h = 0, *t = 0;
307     PyObject* capsule = 0;
308     int status = 0;
309     if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
310     if (!(h = (oskar_Sky*) get_handle(capsule, name))) return 0;
311     t = oskar_sky_create_copy(h, OSKAR_CPU, &status);
312 
313     /* Check for errors. */
314     if (status || !t)
315     {
316         PyErr_Format(PyExc_RuntimeError,
317                 "oskar_sky_create_copy() failed with code %d (%s).",
318                 status, oskar_get_error_string(status));
319         oskar_sky_free(t, &status);
320         return 0;
321     }
322 
323     capsule = PyCapsule_New((void*)t, name, (PyCapsule_Destructor)sky_free);
324     return Py_BuildValue("N", capsule); /* Don't increment refcount. */
325 }
326 
327 
filter_by_flux(PyObject * self,PyObject * args)328 static PyObject* filter_by_flux(PyObject* self, PyObject* args)
329 {
330     oskar_Sky *h = 0;
331     PyObject* capsule = 0;
332     int status = 0;
333     double min_flux_jy = 0.0, max_flux_jy = 0.0;
334     if (!PyArg_ParseTuple(args, "Odd", &capsule, &min_flux_jy, &max_flux_jy))
335         return 0;
336     if (!(h = (oskar_Sky*) get_handle(capsule, name))) return 0;
337 
338     /* Filter the sky model. */
339     oskar_sky_filter_by_flux(h, min_flux_jy, max_flux_jy, &status);
340 
341     /* Check for errors. */
342     if (status)
343     {
344         PyErr_Format(PyExc_RuntimeError,
345                 "oskar_sky_filter_by_flux() failed with code %d (%s).",
346                 status, oskar_get_error_string(status));
347         return 0;
348     }
349     return Py_BuildValue("");
350 }
351 
352 
filter_by_radius(PyObject * self,PyObject * args)353 static PyObject* filter_by_radius(PyObject* self, PyObject* args)
354 {
355     oskar_Sky *h = 0;
356     PyObject* capsule = 0;
357     int status = 0;
358     double inner_radius_rad = 0.0, outer_radius_rad = 0.0;
359     double ra0_rad = 0.0, dec0_rad = 0.0;
360     if (!PyArg_ParseTuple(args, "Odddd", &capsule,
361             &inner_radius_rad, &outer_radius_rad, &ra0_rad, &dec0_rad))
362         return 0;
363     if (!(h = (oskar_Sky*) get_handle(capsule, name))) return 0;
364 
365     /* Filter the sky model. */
366     oskar_sky_filter_by_radius(h, inner_radius_rad, outer_radius_rad,
367             ra0_rad, dec0_rad, &status);
368 
369     /* Check for errors. */
370     if (status)
371     {
372         PyErr_Format(PyExc_RuntimeError,
373                 "oskar_sky_filter_by_radius() failed with code %d (%s).",
374                 status, oskar_get_error_string(status));
375         return 0;
376     }
377     return Py_BuildValue("");
378 }
379 
from_array(PyObject * self,PyObject * args)380 static PyObject* from_array(PyObject* self, PyObject* args)
381 {
382     oskar_Sky* h = 0;
383     PyObject *array_object = 0, *capsule = 0;
384     PyArrayObject* array = 0;
385     npy_intp* dims = 0;
386     const char* type = 0;
387     int status = 0, flags, i, num_dims, num_columns, num_sources = 0, prec;
388     double par[12];
389     if (!PyArg_ParseTuple(args, "Os", &array_object, &type)) return 0;
390 
391     /* Get a handle to the array and its dimensions. */
392     flags = NPY_ARRAY_FORCECAST | NPY_ARRAY_IN_ARRAY;
393     array = (PyArrayObject*) PyArray_FROM_OTF(array_object, NPY_DOUBLE, flags);
394     if (!array) goto fail;
395     num_dims = PyArray_NDIM(array);
396     dims = PyArray_DIMS(array);
397 
398     /* Check dimensions. */
399     if (num_dims > 2)
400     {
401         PyErr_SetString(PyExc_RuntimeError, "Array has too many dimensions.");
402         goto fail;
403     }
404     num_columns = (num_dims == 2) ? (int) dims[1] : (int) dims[0];
405     num_sources = (num_dims == 2) ? (int) dims[0] : 1;
406     if (num_columns < 3)
407     {
408         PyErr_SetString(PyExc_RuntimeError,
409                 "Must specify at least RA, Dec and Stokes I values.");
410         goto fail;
411     }
412     if (num_columns > 12)
413     {
414         PyErr_SetString(PyExc_RuntimeError, "Too many source parameters.");
415         goto fail;
416     }
417 
418     /* Create the sky model. */
419     prec = (type[0] == 'S' || type[0] == 's') ? OSKAR_SINGLE : OSKAR_DOUBLE;
420     h = oskar_sky_create(prec, OSKAR_CPU, 0, &status);
421 
422     /* Set the size of the sky model. */
423     oskar_sky_resize(h, num_sources, &status);
424     if (status)
425     {
426         PyErr_Format(PyExc_RuntimeError,
427                 "oskar_sky_resize() failed with code %d (%s).",
428                 status, oskar_get_error_string(status));
429         goto fail;
430     }
431     if (num_dims == 1)
432     {
433         memset(par, 0, sizeof(par));
434         memcpy(par, PyArray_DATA(array), num_columns * sizeof(double));
435         oskar_sky_set_source(h, 0, par[0] * deg2rad,
436                 par[1] * deg2rad, par[2], par[3], par[4], par[5],
437                 par[6], par[7], par[8], par[9] * arcsec2rad,
438                 par[10] * arcsec2rad, par[11] * deg2rad, &status);
439     }
440     else
441     {
442         for (i = 0; i < num_sources; ++i)
443         {
444             memset(par, 0, sizeof(par));
445             memcpy(par, PyArray_GETPTR2(array, i, 0),
446                     num_columns * sizeof(double));
447             oskar_sky_set_source(h, i, par[0] * deg2rad,
448                     par[1] * deg2rad, par[2], par[3], par[4], par[5],
449                     par[6], par[7], par[8], par[9] * arcsec2rad,
450                     par[10] * arcsec2rad, par[11] * deg2rad, &status);
451             if (status) break;
452         }
453     }
454     if (status)
455     {
456         PyErr_Format(PyExc_RuntimeError,
457                 "oskar_sky_set_source() failed with code %d (%s).",
458                 status, oskar_get_error_string(status));
459         goto fail;
460     }
461 
462     Py_DECREF(array);
463     capsule = PyCapsule_New((void*)h, name, (PyCapsule_Destructor)sky_free);
464     return Py_BuildValue("N", capsule); /* Don't increment refcount. */
465 
466 fail:
467     oskar_sky_free(h, &status);
468     Py_XDECREF(array);
469     return 0;
470 }
471 
472 
from_fits_file(PyObject * self,PyObject * args)473 static PyObject* from_fits_file(PyObject* self, PyObject* args)
474 {
475     oskar_Sky* h = 0;
476     PyObject* capsule = 0;
477     int status = 0, override_units = 0, prec = 0;
478     double frequency_hz, spectral_index, min_peak_fraction, min_abs_val;
479     const char *default_map_units = 0, *filename = 0, *type = 0;
480     if (!PyArg_ParseTuple(args, "sddsidds", &filename,
481             &min_peak_fraction, &min_abs_val, &default_map_units,
482             &override_units, &frequency_hz, &spectral_index, &type))
483         return 0;
484     prec = (type[0] == 'S' || type[0] == 's') ? OSKAR_SINGLE : OSKAR_DOUBLE;
485     h = oskar_sky_from_fits_file(prec, filename, min_peak_fraction,
486             min_abs_val, default_map_units, override_units, frequency_hz,
487             spectral_index, &status);
488 
489     /* Check for errors. */
490     if (status)
491     {
492         PyErr_Format(PyExc_RuntimeError,
493                 "oskar_sky_from_fits_file() failed with code %d (%s).",
494                 status, oskar_get_error_string(status));
495         oskar_sky_free(h, &status);
496         return 0;
497     }
498     capsule = PyCapsule_New((void*)h, name, (PyCapsule_Destructor)sky_free);
499     return Py_BuildValue("N", capsule); /* Don't increment refcount. */
500 }
501 
502 
generate_grid(PyObject * self,PyObject * args)503 static PyObject* generate_grid(PyObject* self, PyObject* args)
504 {
505     oskar_Sky *h = 0;
506     PyObject* capsule = 0;
507     int prec, side_length = 0, seed = 1, status = 0;
508     const char* type = 0;
509     double ra0, dec0, fov, mean_flux_jy, std_flux_jy;
510     if (!PyArg_ParseTuple(args, "ddidddis", &ra0, &dec0, &side_length,
511             &fov, &mean_flux_jy, &std_flux_jy, &seed, &type)) return 0;
512 
513     /* Generate the grid. */
514     prec = (type[0] == 'S' || type[0] == 's') ? OSKAR_SINGLE : OSKAR_DOUBLE;
515     ra0 *= M_PI / 180.0;
516     dec0 *= M_PI / 180.0;
517     fov *= M_PI / 180.0;
518     h = oskar_sky_generate_grid(prec, ra0, dec0, side_length, fov,
519             mean_flux_jy, std_flux_jy, seed, &status);
520 
521     /* Check for errors. */
522     if (status)
523     {
524         PyErr_Format(PyExc_RuntimeError,
525                 "oskar_sky_generate_grid() failed with code %d (%s).",
526                 status, oskar_get_error_string(status));
527         oskar_sky_free(h, &status);
528         return 0;
529     }
530     capsule = PyCapsule_New((void*)h, name, (PyCapsule_Destructor)sky_free);
531     return Py_BuildValue("N", capsule); /* Don't increment refcount. */
532 }
533 
534 
generate_random_power_law(PyObject * self,PyObject * args)535 static PyObject* generate_random_power_law(PyObject* self, PyObject* args)
536 {
537     oskar_Sky *h = 0;
538     PyObject* capsule = 0;
539     int prec, num_sources = 0, seed = 1, status = 0;
540     const char* type = 0;
541     double min_flux_jy = 0.0, max_flux_jy = 0.0, power = 0.0;
542     if (!PyArg_ParseTuple(args, "idddis", &num_sources, &min_flux_jy,
543             &max_flux_jy, &power, &seed, &type)) return 0;
544 
545     /* Generate the sources. */
546     prec = (type[0] == 'S' || type[0] == 's') ? OSKAR_SINGLE : OSKAR_DOUBLE;
547     h = oskar_sky_generate_random_power_law(prec, num_sources,
548             min_flux_jy, max_flux_jy, power, seed, &status);
549 
550     /* Check for errors. */
551     if (status)
552     {
553         PyErr_Format(PyExc_RuntimeError,
554                 "oskar_sky_generate_random_power_law() failed with code %d (%s).",
555                 status, oskar_get_error_string(status));
556         oskar_sky_free(h, &status);
557         return 0;
558     }
559     capsule = PyCapsule_New((void*)h, name, (PyCapsule_Destructor)sky_free);
560     return Py_BuildValue("N", capsule); /* Don't increment refcount. */
561 }
562 
563 
load(PyObject * self,PyObject * args)564 static PyObject* load(PyObject* self, PyObject* args)
565 {
566     oskar_Sky* h = 0;
567     PyObject* capsule = 0;
568     int status = 0, prec = 0;
569     const char *filename = 0, *type = 0;
570     if (!PyArg_ParseTuple(args, "ss", &filename, &type)) return 0;
571     prec = (type[0] == 'S' || type[0] == 's') ? OSKAR_SINGLE : OSKAR_DOUBLE;
572     h = oskar_sky_load(filename, prec, &status);
573 
574     /* Check for errors. */
575     if (status)
576     {
577         PyErr_Format(PyExc_RuntimeError,
578                 "oskar_sky_load() failed with code %d (%s).",
579                 status, oskar_get_error_string(status));
580         oskar_sky_free(h, &status);
581         return 0;
582     }
583     capsule = PyCapsule_New((void*)h, name, (PyCapsule_Destructor)sky_free);
584     return Py_BuildValue("N", capsule); /* Don't increment refcount. */
585 }
586 
587 
num_sources(PyObject * self,PyObject * args)588 static PyObject* num_sources(PyObject* self, PyObject* args)
589 {
590     oskar_Sky *h = 0;
591     PyObject* capsule = 0;
592     if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
593     if (!(h = (oskar_Sky*) get_handle(capsule, name))) return 0;
594     return Py_BuildValue("i", oskar_sky_num_sources(h));
595 }
596 
597 
save(PyObject * self,PyObject * args)598 static PyObject* save(PyObject* self, PyObject* args)
599 {
600     oskar_Sky *h = 0;
601     PyObject* capsule = 0;
602     int status = 0;
603     const char* filename = 0;
604     if (!PyArg_ParseTuple(args, "Os", &capsule, &filename)) return 0;
605     if (!(h = (oskar_Sky*) get_handle(capsule, name))) return 0;
606 
607     /* Save the sky model. */
608 #if OSKAR_VERSION >= 0x020800
609     oskar_sky_save(h, filename, &status);
610 #else
611     oskar_sky_save(filename, h, &status);
612 #endif
613 
614     /* Check for errors. */
615     if (status)
616     {
617         PyErr_Format(PyExc_RuntimeError,
618                 "oskar_sky_save() failed with code %d (%s).",
619                 status, oskar_get_error_string(status));
620         return 0;
621     }
622     return Py_BuildValue("");
623 }
624 
625 
to_array(PyObject * self,PyObject * args)626 static PyObject* to_array(PyObject* self, PyObject* args)
627 {
628     oskar_Sky *h = 0;
629     PyArrayObject* array1 = 0;
630     PyObject* capsule = 0, *array2 = 0;
631     npy_intp dims[2];
632     int num_sources = 0, type = 0;
633     size_t num_bytes;
634     if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
635     if (!(h = (oskar_Sky*) get_handle(capsule, name))) return 0;
636     num_sources = oskar_sky_num_sources(h);
637     type = oskar_sky_precision(h);
638 
639     /* Create a transposed array. */
640     dims[0] = 12;
641     dims[1] = num_sources;
642     array1 = (PyArrayObject*)PyArray_SimpleNew(2, dims,
643             type == OSKAR_DOUBLE ? NPY_DOUBLE : NPY_FLOAT);
644 
645     /* Copy the data into it. */
646     num_bytes = (type == OSKAR_DOUBLE) ? sizeof(double) : sizeof(float);
647     num_bytes *= num_sources;
648     memcpy(PyArray_GETPTR2(array1, 0, 0),
649             oskar_mem_void(oskar_sky_ra_rad(h)), num_bytes);
650     memcpy(PyArray_GETPTR2(array1, 1, 0),
651             oskar_mem_void(oskar_sky_dec_rad(h)), num_bytes);
652     memcpy(PyArray_GETPTR2(array1, 2, 0),
653             oskar_mem_void(oskar_sky_I(h)), num_bytes);
654     memcpy(PyArray_GETPTR2(array1, 3, 0),
655             oskar_mem_void(oskar_sky_Q(h)), num_bytes);
656     memcpy(PyArray_GETPTR2(array1, 4, 0),
657             oskar_mem_void(oskar_sky_U(h)), num_bytes);
658     memcpy(PyArray_GETPTR2(array1, 5, 0),
659             oskar_mem_void(oskar_sky_V(h)), num_bytes);
660     memcpy(PyArray_GETPTR2(array1, 6, 0),
661             oskar_mem_void(oskar_sky_reference_freq_hz(h)), num_bytes);
662     memcpy(PyArray_GETPTR2(array1, 7, 0),
663             oskar_mem_void(oskar_sky_spectral_index(h)), num_bytes);
664     memcpy(PyArray_GETPTR2(array1, 8, 0),
665             oskar_mem_void(oskar_sky_rotation_measure_rad(h)), num_bytes);
666     memcpy(PyArray_GETPTR2(array1, 9, 0),
667             oskar_mem_void(oskar_sky_fwhm_major_rad(h)), num_bytes);
668     memcpy(PyArray_GETPTR2(array1, 10, 0),
669             oskar_mem_void(oskar_sky_fwhm_minor_rad(h)), num_bytes);
670     memcpy(PyArray_GETPTR2(array1, 11, 0),
671             oskar_mem_void(oskar_sky_position_angle_rad(h)), num_bytes);
672 
673     /* Return a transposed copy. */
674     array2 = PyArray_Transpose(array1, 0);
675     Py_DECREF(array1);
676     return Py_BuildValue("N", array2);
677 }
678 
679 
680 /* Method table. */
681 static PyMethodDef methods[] =
682 {
683         {"append", (PyCFunction)append, METH_VARARGS, "append(sky)"},
684         {"append_sources", (PyCFunction)append_sources, METH_VARARGS,
685                 "append_sources(ra, dec, I, Q, U, V, ref_freq, spectral_index, "
686                 "rotation_measure, major, minor, position_angle)"},
687         {"append_file", (PyCFunction)append_file,
688                 METH_VARARGS, "append_file(filename)"},
689         {"capsule_name", (PyCFunction)capsule_name,
690                 METH_VARARGS, "capsule_name()"},
691         {"create", (PyCFunction)create, METH_VARARGS, "create(precision)"},
692         {"create_copy", (PyCFunction)create_copy,
693                 METH_VARARGS, "create_copy(sky)"},
694         {"filter_by_flux", (PyCFunction)filter_by_flux,
695                 METH_VARARGS, "filter_by_flux(min_flux_jy, max_flux_jy)"},
696         {"filter_by_radius", (PyCFunction)filter_by_radius,
697                 METH_VARARGS, "filter_by_radius(inner_radius_rad, "
698                 "outer_radius_rad, ra0_rad, dec0_rad)"},
699         {"from_array", (PyCFunction)from_array,
700                 METH_VARARGS, "from_array(array)"},
701         {"from_fits_file", (PyCFunction)from_fits_file,
702                 METH_VARARGS, "from_fits_file(filename, min_peak_fraction, "
703                 "min_abs_val, default_map_units, override_map_units, "
704                 "frequency_hz, spectral_index, precision)"},
705         {"generate_grid", (PyCFunction)generate_grid,
706                 METH_VARARGS, "generate_grid(ra0, dec0, side_length, "
707                 "fov, mean_flux_jy, std_flux_jy, seed, precision)"},
708         {"generate_random_power_law", (PyCFunction)generate_random_power_law,
709                 METH_VARARGS, "generate_random_power_law(num_sources, "
710                 "min_flux_jy, max_flux_jy, power, seed, precision)"},
711         {"load", (PyCFunction)load, METH_VARARGS, "load(filename, precision)"},
712         {"num_sources", (PyCFunction)num_sources,
713                 METH_VARARGS, "num_sources()"},
714         {"save", (PyCFunction)save, METH_VARARGS, "save(filename)"},
715         {"to_array", (PyCFunction)to_array, METH_VARARGS, "to_array()"},
716         {NULL, NULL, 0, NULL}
717 };
718 
719 
720 #if PY_MAJOR_VERSION >= 3
721 static PyModuleDef moduledef = {
722         PyModuleDef_HEAD_INIT,
723         "_sky_lib",         /* m_name */
724         module_doc,         /* m_doc */
725         -1,                 /* m_size */
726         methods             /* m_methods */
727 };
728 #endif
729 
730 
moduleinit(void)731 static PyObject* moduleinit(void)
732 {
733     PyObject* m;
734 #if PY_MAJOR_VERSION >= 3
735     m = PyModule_Create(&moduledef);
736 #else
737     m = Py_InitModule3("_sky_lib", methods, module_doc);
738 #endif
739     return m;
740 }
741 
742 #if PY_MAJOR_VERSION >= 3
PyInit__sky_lib(void)743 PyMODINIT_FUNC PyInit__sky_lib(void)
744 {
745     import_array();
746     return moduleinit();
747 }
748 #else
749 /* The init function name has to match that of the compiled module
750  * with the pattern 'init<module name>'. This module is called '_sky_lib' */
init_sky_lib(void)751 PyMODINIT_FUNC init_sky_lib(void)
752 {
753     import_array();
754     moduleinit();
755     return;
756 }
757 #endif
758 
759