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