Ticket 720: WRWP, adjustement to be able to cope with volumes only having scans with...
[baltrad-wrwp.git] / pywrwp / pywrwp.c
1 /* --------------------------------------------------------------------
2 Copyright (C) 2013 Swedish Meteorological and Hydrological Institute, SMHI
3
4 This is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This software is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with baltrad-wrwp.  If not, see <http://www.gnu.org/licenses/>.
16 ------------------------------------------------------------------------*/
17
18 /**
19  * Python module
20  * @file
21  * @author Anders Henja, SMHI
22  * @date 2013-09-17
23  */
24 #include <Python.h>
25 #include <math.h>
26 #include <stdio.h>
27 #include <string.h>
28
29 #define PYWRWP_MODULE /**< include correct part of pywrwp.h */
30 #include "pywrwp.h"
31
32 #include <pypolarvolume.h>
33 #include <pyverticalprofile.h>
34 #include "wrwp.h"
35
36 #include <arrayobject.h>
37 #include "pyrave_debug.h"
38 #include "rave_alloc.h"
39 #include "rave.h"
40
41 /**
42  * Debug this module
43  */
44 PYRAVE_DEBUG_MODULE("_wrwp");
45
46 /**
47  * Sets a python exception and goto tag
48  */
49 #define raiseException_gotoTag(tag, type, msg) \
50 {PyErr_SetString(type, msg); goto tag;}
51
52 /**
53  * Sets python exception and returns NULL
54  */
55 #define raiseException_returnNULL(type, msg) \
56 {PyErr_SetString(type, msg); return NULL;}
57
58 /**
59  * Error object for reporting errors to the python interpreeter
60  */
61 static PyObject* ErrorObject;
62
63
64 /*@{ Weather radar wind profiles */
65
66 /**
67  * Returns the native Wrwp_t instance.
68  * @param[in] pywrwp - the python wrwp instance
69  * @returns the native wrwp instance.
70  */
71 static Wrwp_t*
72 PyWrwp_GetNative(PyWrwp* pywrwp)
73 {
74   RAVE_ASSERT((pywrwp != NULL), "pywrwp == NULL");
75   return RAVE_OBJECT_COPY(pywrwp->wrwp);
76 }
77
78 /**
79  * Creates a python wrwp from a native wrwp or will create an
80  * initial native wrwp if p is NULL.
81  * @param[in] p - the native wrwp (or NULL)
82  * @returns the python wrwp product generator.
83  */
84 static PyWrwp*
85 PyWrwp_New(Wrwp_t* p)
86 {
87   PyWrwp* result = NULL;
88   Wrwp_t* cp = NULL;
89
90   if (p == NULL) {
91     cp = RAVE_OBJECT_NEW(&Wrwp_TYPE);
92     if (cp == NULL) {
93       RAVE_CRITICAL0("Failed to allocate memory for wrwp.");
94       raiseException_returnNULL(PyExc_MemoryError, "Failed to allocate memory for wrwp.");
95     }
96   } else {
97     cp = RAVE_OBJECT_COPY(p);
98     result = RAVE_OBJECT_GETBINDING(p); // If p already have a binding, then this should only be increfed.
99     if (result != NULL) {
100       Py_INCREF(result);
101     }
102   }
103
104   if (result == NULL) {
105     result = PyObject_NEW(PyWrwp, &PyWrwp_Type);
106     if (result != NULL) {
107       PYRAVE_DEBUG_OBJECT_CREATED;
108       result->wrwp = RAVE_OBJECT_COPY(cp);
109       RAVE_OBJECT_BIND(result->wrwp, result);
110     } else {
111       RAVE_CRITICAL0("Failed to create PyWrwp instance");
112       raiseException_gotoTag(done, PyExc_MemoryError, "Failed to allocate memory for PyWrwp.");
113     }
114   }
115
116 done:
117   RAVE_OBJECT_RELEASE(cp);
118   return result;
119 }
120
121 /**
122  * Deallocates the wrwp generator
123  * @param[in] obj the object to deallocate.
124  */
125 static void _pywrwp_dealloc(PyWrwp* obj)
126 {
127   /*Nothing yet*/
128   if (obj == NULL) {
129     return;
130   }
131   PYRAVE_DEBUG_OBJECT_DESTROYED;
132   RAVE_OBJECT_UNBIND(obj->wrwp, obj);
133   RAVE_OBJECT_RELEASE(obj->wrwp);
134   PyObject_Del(obj);
135 }
136
137 /**
138  * Creates a new instance of the wrwp generator.
139  * @param[in] self this instance.
140  * @param[in] args arguments for creation (NOT USED).
141  * @return the object on success, otherwise NULL
142  */
143 static PyObject* _pywrwp_new(PyObject* self, PyObject* args)
144 {
145   PyWrwp* result = PyWrwp_New(NULL);
146   return (PyObject*)result;
147 }
148
149
150 static PyObject* _pywrwp_generate(PyWrwp* self, PyObject* args)
151 {
152   PyObject* obj = NULL;
153   PyVerticalProfile* pyvp = NULL;
154   VerticalProfile_t* vp = NULL;
155   char* fieldsToGenerate = NULL;
156
157   if(!PyArg_ParseTuple(args, "O|z", &obj, &fieldsToGenerate)) {
158     return NULL;
159   }
160
161   if (!PyPolarVolume_Check(obj)) {
162     raiseException_returnNULL(PyExc_AttributeError, "In argument must be a polar volume");
163   }
164
165   vp = Wrwp_generate(self->wrwp, ((PyPolarVolume*)obj)->pvol, fieldsToGenerate);
166
167   if (vp == NULL) {
168     raiseException_gotoTag(done, PyExc_RuntimeError, "Failed to generate vertical profile");
169   }
170
171   pyvp = PyVerticalProfile_New(vp);
172
173   RAVE_OBJECT_RELEASE(vp);
174     
175 done:
176   RAVE_OBJECT_RELEASE(vp);
177   return (PyObject*)pyvp;
178 }
179
180 /**
181  * All methods a wrwp generator can have
182  */
183 static struct PyMethodDef _pywrwp_methods[] =
184 {
185   {"generate", (PyCFunction)_pywrwp_generate, 1},
186   {NULL, NULL } /* sentinel */
187 };
188
189 /**
190  * Returns the specified attribute in the wrwp generator
191  * @param[in] self - the wrwp generator
192  */
193 static PyObject* _pywrwp_getattr(PyWrwp* self, char* name)
194 {
195   PyObject* res = NULL;
196   if (strcmp("dz", name) == 0) {
197     return PyInt_FromLong(Wrwp_getDZ(self->wrwp));
198   } else if (strcmp("hmax", name) == 0) {
199     return PyInt_FromLong(Wrwp_getHMAX(self->wrwp));
200   } else if (strcmp("dmin", name) == 0) {
201     return PyInt_FromLong(Wrwp_getDMIN(self->wrwp));
202   } else if (strcmp("dmax", name) == 0) {
203     return PyInt_FromLong(Wrwp_getDMAX(self->wrwp));
204   } else if (strcmp("emin", name) == 0) {
205     return PyFloat_FromDouble(Wrwp_getEMIN(self->wrwp));
206   } else if (strcmp("vmin", name) == 0) {
207     return PyFloat_FromDouble(Wrwp_getVMIN(self->wrwp));
208   }
209   res = Py_FindMethod(_pywrwp_methods, (PyObject*) self, name);
210   if (res)
211     return res;
212
213   PyErr_Clear();
214   PyErr_SetString(PyExc_AttributeError, name);
215   return NULL;
216 }
217
218 /**
219  * Returns the specified attribute in the wrwp generator
220  */
221 static int _pywrwp_setattr(PyWrwp* self, char* name, PyObject* val)
222 {
223   int result = -1;
224   if (name == NULL) {
225     goto done;
226   }
227   if (strcmp("dz", name) == 0) {
228     if (PyInt_Check(val)) {
229       Wrwp_setDZ(self->wrwp, PyInt_AsLong(val));
230     } else {
231       raiseException_gotoTag(done, PyExc_TypeError, "dz must be an integer");
232     }
233   } else if (strcmp("hmax", name) == 0) {
234     if (PyInt_Check(val)) {
235       Wrwp_setHMAX(self->wrwp, PyInt_AsLong(val));
236     } else {
237       raiseException_gotoTag(done, PyExc_TypeError, "hmax must be an integer");
238     }
239   } else if (strcmp("dmin", name) == 0) {
240     if (PyInt_Check(val)) {
241       Wrwp_setDMIN(self->wrwp, PyInt_AsLong(val));
242     } else {
243       raiseException_gotoTag(done, PyExc_TypeError, "dmin must be an integer");
244     }
245   } else if (strcmp("dmax", name) == 0) {
246     if (PyInt_Check(val)) {
247       Wrwp_setDMAX(self->wrwp, PyInt_AsLong(val));
248     } else {
249       raiseException_gotoTag(done, PyExc_TypeError, "dmax must be an integer");
250     }
251   } else if (strcmp("emin", name) == 0) {
252     if (PyFloat_Check(val)) {
253       Wrwp_setEMIN(self->wrwp, PyFloat_AsDouble(val));
254     } else if (PyInt_Check(val)) {
255       Wrwp_setEMIN(self->wrwp, (double)PyInt_AsLong(val));
256     } else {
257       raiseException_gotoTag(done, PyExc_TypeError, "emin must be an integer or a float");
258     }
259   } else if (strcmp("vmin", name) == 0) {
260     if (PyFloat_Check(val)) {
261       Wrwp_setVMIN(self->wrwp, PyFloat_AsDouble(val));
262     } else if (PyInt_Check(val)) {
263       Wrwp_setVMIN(self->wrwp, (double)PyInt_AsLong(val));
264     } else {
265       raiseException_gotoTag(done, PyExc_TypeError, "vmin must be an integer or a float");
266     }
267   }
268
269   result = 0;
270 done:
271   return result;
272 }
273 /*@} End of Weather radar wind profiles */
274
275 /*@{ Type definitions */
276 PyTypeObject PyWrwp_Type =
277 {
278   PyObject_HEAD_INIT(NULL)0, /*ob_size*/
279   "WrwpCore", /*tp_name*/
280   sizeof(PyWrwp), /*tp_size*/
281   0, /*tp_itemsize*/
282   /* methods */
283   (destructor)_pywrwp_dealloc, /*tp_dealloc*/
284   0, /*tp_print*/
285   (getattrfunc)_pywrwp_getattr, /*tp_getattr*/
286   (setattrfunc)_pywrwp_setattr, /*tp_setattr*/
287   0, /*tp_compare*/
288   0, /*tp_repr*/
289   0, /*tp_as_number */
290   0,
291   0, /*tp_as_mapping */
292   0 /*tp_hash*/
293 };
294
295 /*@} End of Type definitions */
296
297 /// --------------------------------------------------------------------
298 /// Module setup
299 /// --------------------------------------------------------------------
300 /*@{ Module setup */
301 static PyMethodDef functions[] = {
302   {"new", (PyCFunction)_pywrwp_new, 1},
303   {NULL,NULL} /*Sentinel*/
304 };
305
306 PyMODINIT_FUNC
307 init_wrwp(void)
308 {
309   PyObject *module=NULL,*dictionary=NULL;
310   static void *PyWrwp_API[PyWrwp_API_pointers];
311   PyObject *c_api_object = NULL;
312   PyWrwp_Type.ob_type = &PyType_Type;
313
314   module = Py_InitModule("_wrwp", functions);
315   if (module == NULL) {
316     return;
317   }
318   PyWrwp_API[PyWrwp_Type_NUM] = (void*)&PyWrwp_Type;
319   PyWrwp_API[PyWrwp_GetNative_NUM] = (void *)PyWrwp_GetNative;
320   PyWrwp_API[PyWrwp_New_NUM] = (void*)PyWrwp_New;
321
322   c_api_object = PyCObject_FromVoidPtr((void *)PyWrwp_API, NULL);
323
324   if (c_api_object != NULL) {
325     PyModule_AddObject(module, "_C_API", c_api_object);
326   }
327
328   dictionary = PyModule_GetDict(module);
329   ErrorObject = PyString_FromString("_wrwp.error");
330   if (ErrorObject == NULL || PyDict_SetItemString(dictionary, "error", ErrorObject) != 0) {
331     Py_FatalError("Can't define _wrwp.error");
332   }
333
334   import_array();
335   import_pypolarvolume();
336   import_pyverticalprofile();
337   PYRAVE_DEBUG_INITIALIZE;
338 }
339 /*@} End of Module setup */