source: trunk/src/python/PyRpUnits.cc @ 116

Last change on this file since 116 was 116, checked in by dkearney, 17 years ago
  1. rewrote RpUnits::define(...) and RpUnits::convert(...) fxns.
  2. added functionality so you no longer need to call add_presets(...)
  3. RpUnits can now handle conversions as follows 1cm2/Vs -> 1e-7m2/kVus
  4. Cannot handle conversions dealing with Temperature very well because

Temperature conversions have offsets (+/- 32...). you can still do
F->C and Fs->Cs, but Fms->Cs provides unreliable results. (not to
mention that i'm still unsure how to do a conversion like this.

  1. still need to add Fo (delta fahrenheit) and Co (delta celcius)

units and conversions. These should not be effected by the notorious
temperature

  1. adjusted RpUnits_test.cc for testing. python.fortran and matlab

bindings have not been tested yet.

File size: 16.2 KB
Line 
1/*
2 * ======================================================================
3 *  Copyright (c) 2004-2005  Purdue Research Foundation
4 *
5 *  See the file "license.terms" for information on usage and
6 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
7 * ======================================================================
8 */
9#include <Python.h>
10#include <RpUnits.h>
11
12static PyObject *ErrorObject;
13
14typedef struct {
15    PyObject_HEAD
16    const RpUnits* rp_unit;
17} RpUnitsObject;
18
19static void
20RpUnitsObject_dealloc(RpUnitsObject *self)
21{
22    if (self) {
23        if (self->rp_unit){
24            // cant call this right now because destructor is private
25            // delete(self->rp_unit);
26        }
27        PyObject_Del(self);
28    }
29}
30
31static PyObject*
32RpUnitsObject_getUnits(RpUnitsObject *self)
33{
34    PyObject* rv = NULL;
35
36    if (self->rp_unit){
37        rv = PyString_FromString(self->rp_unit->getUnits().c_str());
38    }
39
40    return rv;
41}
42
43static PyObject*
44RpUnitsObject_getUnitsName(RpUnitsObject *self)
45{
46    PyObject* rv = NULL;
47
48    if (self->rp_unit){
49        rv = PyString_FromString(self->rp_unit->getUnitsName().c_str());
50    }
51
52    return rv;
53}
54
55static PyObject*
56RpUnitsObject_getExponent(RpUnitsObject *self)
57{
58    PyObject* rv = NULL;
59
60    if (self->rp_unit){
61        rv = PyFloat_FromDouble(self->rp_unit->getExponent());
62    }
63
64    return rv;
65}
66
67static PyObject * RpUnitsObject_convert(RpUnitsObject *self, PyObject *args);
68
69static PyObject *
70RpUnitsObject_makeBasis(RpUnitsObject *self, PyObject *args)
71{
72    PyObject* rv = NULL;
73    double inVal = 0;
74    double outVal = 0;
75    int result = 0;
76
77    if (PyTuple_Size(args) > 0) {
78        PyArg_ParseTuple(args, "d", &inVal);
79    }
80    else {
81        PyErr_SetString(PyExc_AttributeError, "incorrect input arguments");
82        return NULL;
83    }
84
85    if (self->rp_unit){
86        outVal = self->rp_unit->makeBasis(inVal, &result);
87        if (result) {
88            rv = PyFloat_FromDouble(outVal);
89        }
90        else {
91            PyErr_SetString(PyExc_StandardError, "could not convert to basis");
92        }
93    }
94    else {
95        PyErr_SetString(PyExc_AttributeError, "rp_unit is NULL");
96    }
97
98    return rv;
99}
100
101static PyMethodDef RpUnitsObject_methods[] = {
102    {"getUnits", (PyCFunction)RpUnitsObject_getUnits, METH_NOARGS,
103     "Return the base name of the RpUnitsObject" },
104    {"getUnitsName", (PyCFunction)RpUnitsObject_getUnitsName, METH_NOARGS,
105     "Return the whole (base and exponent) name of the RpUnitsObject" },
106    {"getExponent", (PyCFunction)RpUnitsObject_getExponent, METH_NOARGS,
107     "Return the exponent of the RpUnitsObject" },
108    {"convert", (PyCFunction)RpUnitsObject_convert, METH_VARARGS,
109     "convert a value from one RpUnits Object to another" },
110    {"makeBasis", (PyCFunction)RpUnitsObject_makeBasis, METH_VARARGS,
111     "return the basis value of the value provided" },
112
113    {NULL}  /* Sentinel */
114};
115
116static PyTypeObject RpUnitsObjectType = {
117    /* The ob_type field must be initialized in the module init function
118     * to be portable to Windows without using C++. */
119    PyObject_HEAD_INIT(NULL)
120    0,                                    /*ob_size*/
121    "RpUnits.RpUnitsObject",              /*tp_name*/
122    sizeof(RpUnitsObject),                /*tp_basicsize*/
123    0,                                    /*tp_itemsize*/
124    /* methods */
125    (destructor)RpUnitsObject_dealloc,    /*tp_dealloc*/
126    0,                                    /*tp_print*/
127    0,                                    /*tp_getattr*/
128    0,                                    /*tp_setattr*/
129    0,                                    /*tp_compare*/
130    0,                                    /*tp_repr*/
131    0,                                    /*tp_as_number*/
132    0,                                    /*tp_as_sequence*/
133    0,                                    /*tp_as_mapping*/
134    0,                                    /*tp_hash*/
135    0,                                    /*tp_call*/
136    0,                                    /*tp_str*/
137    0,                                    /*tp_getattro*/
138    0,                                    /*tp_setattro*/
139    0,                                    /*tp_as_buffer*/
140    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /*tp_flags*/
141    "Rappture.Units Object",              /*tp_doc*/
142    0,                                    /*tp_traverse*/
143    0,                                    /*tp_clear*/
144    0,                                    /*tp_richcompare*/
145    0,                                    /*tp_weaklistoffset*/
146    0,                                    /*tp_iter*/
147    0,                                    /*tp_iternext*/
148    RpUnitsObject_methods,                /* tp_methods */
149    0,                                    /*tp_members*/
150    0,                                    /*tp_getset*/
151    0,                                    /*tp_base*/
152    0,                                    /*tp_dict*/
153    0,                                    /*tp_descr_get*/
154    0,                                    /*tp_descr_set*/
155    0,                                    /*tp_dictoffset*/
156    0,                                    /*tp_init*/
157    0,                                    /*tp_alloc*/
158    // RpUnitsObject_new,                 /*tp_new*/
159    0,                                    /*tp_new*/
160    0,                                    /*tp_free*/
161    0,                                    /*tp_is_gc*/
162};
163
164static RpUnitsObject*
165newRpUnitsObject(PyObject *arg)
166{
167    RpUnitsObject* self;
168    self = PyObject_New(RpUnitsObject, &RpUnitsObjectType);
169    if (self == NULL)
170        return NULL;
171    self->rp_unit = NULL;
172    return self;
173}
174
175static PyObject *
176RpUnitsObject_convert(RpUnitsObject *self, PyObject *args)
177{
178    PyObject* rv = NULL;
179    // PyObject* toUnits = NULL;
180    RpUnitsObject* toUnits = NULL;
181    PyObject* inVal = NULL;
182    PyObject* argList = NULL;
183    PyObject* outVal = NULL;
184    int result = 0;
185    int argTupleSize = PyTuple_Size(args);
186
187    if (argTupleSize > 0) {
188        PyArg_ParseTuple(args, "O!O", &RpUnitsObjectType, &toUnits, &inVal);
189        argList = PyTuple_New(1);
190        PyTuple_SetItem(argList, 0, inVal);
191        /*
192         * need to make it so user can give any number of variables in arglist
193         * because the new argList is sent to the python conversion fxn where it
194         * will be parsed in python when c++ calls the conv fxn.
195        PyArg_ParseTuple(args, "O!", &RpUnitsObjectType, &toUnits);
196        if (argTupleSize > 1) {
197            argTupleSize--;
198            inVal = PyTuple_New(argTupleSize);
199            while (argTupleSize)
200            inVal = PyTuple_GetSlice(args,0,argTupleSize);
201        }
202        else {
203            // convertion function needs no arguments???
204            inVal = PyTuple_New(0);
205        }
206        */
207    }
208    else {
209        PyErr_SetString(PyExc_AttributeError, "Not enough arguments");
210        return Py_None;
211    }
212
213    if (self->rp_unit){
214        outVal = (PyObject*) self->rp_unit->convert(toUnits->rp_unit,
215                                                    // (void*)&inVal,
216                                                    (void*)argList,
217                                                    &result );
218        if (result) {
219            rv = outVal;
220        }
221    }
222
223    return rv;
224}
225
226/* --------------------------------------------------------------------- */
227
228PyDoc_STRVAR(RpUnits_define_doc,
229"define(name, basis) -> RpUnitsObject \n\
230\n\
231define RpUnits Object where 'name' is the string name of the units \n\
232and 'basis' is an RpUnitsObject pointer for the basis of the unit \n\
233being created");
234
235static PyObject *
236RpUnits_define(PyObject *self, PyObject *args)
237{
238    RpUnitsObject* newRpUnit;
239    RpUnitsObject* basis = NULL;
240    char* unitsName;
241
242    if (PyTuple_Size(args) > 0) {
243        PyArg_ParseTuple(args, "s|O!", &unitsName, &RpUnitsObjectType,&basis);
244    }
245    else {
246        PyErr_SetString(PyExc_AttributeError, "Not enough arguments");
247        return Py_None;
248    }
249
250    newRpUnit = newRpUnitsObject(args);
251
252    if (newRpUnit == NULL)
253        return NULL;
254
255
256    if (basis && basis->rp_unit) {
257        newRpUnit->rp_unit = RpUnits::define(unitsName, basis->rp_unit);
258    }
259    else {
260        newRpUnit->rp_unit = RpUnits::define(unitsName, NULL);
261    }
262
263    if (newRpUnit->rp_unit == NULL) {
264        // rp_unit was not allocated.
265        RpUnitsObject_dealloc(newRpUnit);
266        PyErr_SetString(PyExc_AttributeError, "allocating rp_unit failed");
267        return NULL;
268    }
269
270    return (PyObject *)newRpUnit;
271}
272
273void* PyCallback (void* fxnPtr, void* args)
274{
275    PyObject* retVal = NULL;
276
277    if ((PyObject*)fxnPtr != NULL) {
278        retVal = PyObject_CallObject((PyObject*)fxnPtr,(PyObject*)args);
279    }
280
281    return (void*) retVal;
282
283}
284
285PyDoc_STRVAR(RpUnits_defineConv_doc,
286"defineConv(fromUnits,toUnits, forwConvFxn, backConvFxn) -> RpUnitsObject\n\
287\n\
288define RpUnits Object where 'name' is the string name of the units \n\
289and 'basis' is an RpUnitsObject pointer for the basis of the unit \n\
290being created");
291
292static PyObject *
293RpUnits_defineConv(PyObject *self, PyObject *args)
294{
295    RpUnitsObject* fromUnit = NULL;
296    RpUnitsObject* toUnit = NULL;
297    PyObject* forwConvFxnStr = NULL;
298    PyObject* backConvFxnStr = NULL;
299
300    RpUnitsObject* newRpUnit = NULL;
301    const RpUnits* newConv = NULL;
302
303    if (PyTuple_Size(args) > 0) {
304        PyArg_ParseTuple(args, "O!O!O!O!",&RpUnitsObjectType, &fromUnit,
305                                          &RpUnitsObjectType, &toUnit,
306                                          &PyFunction_Type, &forwConvFxnStr,
307                                          &PyFunction_Type, &backConvFxnStr);
308    }
309    else {
310        PyErr_SetString(PyExc_AttributeError, "incorrect input arguments");
311        return NULL;
312    }
313
314    // check to make sure fromUnit and toUnit are populated
315    if ( (fromUnit == NULL) ||
316         (toUnit == NULL) ) {
317        PyErr_SetString(PyExc_AttributeError,
318                "could not retrieve fromUnit or toUnit from argument list");
319    }
320
321    // check to make sure forwConvFxnStr and backConvFxnStr are populated
322    if ( (forwConvFxnStr == NULL) || (backConvFxnStr == NULL) ) {
323        PyErr_SetString(PyExc_AttributeError,
324                "could not retrieve conversion function argument");
325        return NULL;
326    }
327
328    // make sure we get callable functions and non-null RpUnit Objects
329    if ( PyCallable_Check(forwConvFxnStr) &&
330         PyCallable_Check(backConvFxnStr) &&
331         fromUnit->rp_unit &&
332         toUnit->rp_unit) {
333
334        Py_INCREF(forwConvFxnStr);
335        Py_INCREF(backConvFxnStr);
336        newConv = RpUnits::define(  fromUnit->rp_unit,
337                                    toUnit->rp_unit,
338                                    PyCallback,
339                                    (void*)forwConvFxnStr,
340                                    PyCallback,
341                                    (void*)backConvFxnStr );
342    }
343    else {
344        PyErr_SetString(PyExc_AttributeError,
345                "could not retrieve conversion function argument");
346        return NULL;
347    }
348
349    if (newConv) {
350        newRpUnit = newRpUnitsObject(args);
351
352        if (newRpUnit) {
353            newRpUnit->rp_unit = newConv;
354        }
355    }
356
357    return (PyObject *)newRpUnit;
358}
359
360PyDoc_STRVAR(RpUnits_find_doc,
361"find(name) -> RpUnitsObject \n\
362\n\
363search the dictionary of created RpUnits for a unit matching \n\
364the string 'name'");
365
366static PyObject *
367RpUnits_find(PyObject *self, PyObject *args)
368{
369    char* searchUnits = NULL;
370    const RpUnits* foundUnits = NULL;
371    RpUnitsObject* returnUnits = NULL;
372
373    if (PyTuple_Size(args) > 0) {
374        PyArg_ParseTuple(args, "s", &searchUnits);
375    }
376    else {
377        PyErr_SetString(PyExc_AttributeError, "incorrect input arguments");
378        return NULL;
379    }
380
381    foundUnits = RpUnits::find(searchUnits);
382
383    if (foundUnits) {
384        returnUnits = newRpUnitsObject(args);
385
386        if (returnUnits == NULL)
387            return NULL;
388
389        returnUnits->rp_unit = foundUnits;
390    }
391
392    return (PyObject*) returnUnits;
393
394}
395
396PyDoc_STRVAR(RpUnits_makeMetric_doc,
397"makeMetric (newBasis) -> PyInt \n\
398\n\
399Create the metric extentions and conversion functions for \n\
400the unit 'newBasis', thus making it a new basis.");
401
402static PyObject*
403RpUnits_makeMetric(PyObject *self, PyObject *args)
404{
405    RpUnitsObject* units = NULL;
406    int result = 0;
407
408    if (PyTuple_Size(args) > 0) {
409        PyArg_ParseTuple(args, "O!", &RpUnitsObjectType, &units);
410    }
411    else {
412        return NULL;
413    }
414
415    if (units->rp_unit) {
416        result = RpUnits::makeMetric(units->rp_unit);
417    }
418
419    return PyInt_FromLong((long)result);
420}
421
422PyDoc_STRVAR(RpUnits_convert_doc,
423"convert (fromVal, to, units) -> PyString \n\
424\n\
425Convert the value 'fromVal', to the units listed in 'to', \n\
426and return the value as a string. The string 'fromVal' must have a \n\
427numeric value with units attached to the end. If 'units' is set to \n\
428'off' then the returned string will not have units. The default \n\
429behavior is to show units.");
430
431static PyObject*
432RpUnits_convert(PyObject *self, PyObject *args, PyObject *keywds)
433{
434    // RpUnitsObject* units = NULL;
435    char* fromVal = NULL;
436    char* to = NULL;
437    char* units = NULL;
438    // char* tmpRetStr = NULL;
439    std::string fromVal_S = "";
440    std::string to_S = "";
441    std::string tmpUnits_S = "";
442    int unitsVal = 1;
443    int result = 0;
444    std::string retStr = "";
445    PyObject* retVal = NULL;
446    PyObject* tmpPyStr = NULL;
447
448    static char *kwlist[] = {"fromVal", "to", "units", NULL};
449
450    if (PyTuple_Size(args) > 0) {
451        // PyArg_ParseTuple(args, "ss|s", &fromVal, &to, &units);
452        if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss|s",
453                    kwlist, &fromVal, &to, &units)) {
454            return NULL;
455        }
456    }
457    else {
458        return NULL;
459    }
460
461    fromVal_S = std::string(fromVal);
462    to_S = std::string(to);
463    if (units) {
464        tmpUnits_S = std::string(units);
465        if(tmpUnits_S.compare("off") == 0) {
466            unitsVal = 0;
467        }
468        else {
469            unitsVal = 1;
470        }
471    }
472
473    retStr = RpUnits::convert(fromVal_S,to_S,unitsVal,&result);
474
475    std::cout << result << std::endl;
476    if ( (!retStr.empty()) && (result == 0) ) {
477        if (unitsVal) {
478            retVal = PyString_FromString(retStr.c_str());
479        }
480        else {
481            // convert to a double and return that if
482            // the units were turned off
483            tmpPyStr = PyString_FromString(retStr.c_str());
484            if (tmpPyStr) {
485                    Py_INCREF(tmpPyStr);
486                    retVal = PyFloat_FromString(tmpPyStr,NULL);
487                    Py_DECREF(tmpPyStr);
488            }
489        }
490    }
491    else {
492        //keeping this around in case you want string returned instead of None
493        //if (fromVal) {
494        //    retVal = PyString_FromString(fromVal);
495        //}
496        //else {
497        retVal = Py_None;
498        Py_INCREF(retVal);
499        //}
500    }
501
502    return retVal;
503}
504
505/* ---------- */
506
507
508/* List of functions defined in the module */
509
510static PyMethodDef RpUnits_Methods[] = {
511
512    {"define", RpUnits_define, METH_VARARGS,
513        RpUnits_define_doc},
514
515    {"defineConv", RpUnits_defineConv, METH_VARARGS,
516        RpUnits_defineConv_doc},
517
518    {"find", RpUnits_find, METH_VARARGS,
519        RpUnits_find_doc},
520
521    {"makeMetric", RpUnits_makeMetric, METH_VARARGS,
522        RpUnits_makeMetric_doc},
523
524    {"convert", (PyCFunction)RpUnits_convert, METH_VARARGS|METH_KEYWORDS,
525        RpUnits_convert_doc},
526
527    {NULL,        NULL}        /* sentinel */
528};
529
530PyDoc_STRVAR(module_doc, "RpUnits Module for Python.");
531
532/* Initialization function for the module */
533
534PyMODINIT_FUNC
535initUnits(void)
536{
537    PyObject *m;
538
539    /* Finalize the type object including setting type of the new type
540     * object; doing it here is required for portability to Windows
541     * without requiring C++. */
542    if (PyType_Ready(&RpUnitsObjectType) < 0)
543        return;
544
545    /* Create the module and add the functions */
546    m = Py_InitModule3("Units", RpUnits_Methods, module_doc);
547
548    /* Add some symbolic constants to the module */
549    if (ErrorObject == NULL) {
550        ErrorObject = PyErr_NewException("RpUnits.error", NULL, NULL);
551        if (ErrorObject == NULL)
552            return;
553    }
554    Py_INCREF(ErrorObject);
555    PyModule_AddObject(m, "error", ErrorObject);
556
557    // add some standard units definitions and conversions.
558    // RpUnits::addPresets("all");
559}
Note: See TracBrowser for help on using the repository browser.