OPAL (Object Oriented Parallel Accelerator Library) 2022.1
OPAL
PyOpalObject.h
Go to the documentation of this file.
1#ifndef PyOpalObject_H
2
3#include <Python.h>
4#include <structmember.h>
5
6#include <memory>
7#include <exception>
8#include <iostream>
9#include <boost/python.hpp>
10#include <boost/noncopyable.hpp>
11#include <boost/mpl/front.hpp>
12
20
21namespace PyOpal {
22
47namespace PyOpalObjectNS {
48
49// forward declarations
50template <class C> class PyOpalObject;
51template <class C> struct PyOpalObjectGetProperty;
52template <class C> struct PyOpalObjectSetProperty;
53
62
64extern std::map<AttributeType, std::string> attributeName; // defined in PyOpalObject.cpp
65
75 std::string opalName_m;
76 std::string pyName_m;
77 std::string docString_m;
79 // could also add a "read only" flag as some attributes are read only
80};
81
117template <class C>
118class PyOpalObject {
119public:
120 typedef PyOpalObject<C> PyC; // for convenience
122 inline PyOpalObject();
124 PyOpalObject(std::shared_ptr<C> object) : object_m(object) {}
126 PyOpalObject(const PyOpalObject<C>& rhs);
129
136 inline boost::python::class_<PyC> make_class(const char* className);
137
140 template <class PYCLASS>
141 void addAttributes(PYCLASS& pyclass);
142
145 template <class PYCLASS>
146 void addExecute(PYCLASS& pyclass);
147
150 template <class PYCLASS>
151 void addRegister(PYCLASS& pyclass);
152
155 template <class PYCLASS>
156 void addGetOpalElement(PYCLASS& pyclass);
157
161 template <class ValueType>
162 ValueType dummyGet() const {PyOpalObjectGetProperty<C>::setObject(this); return ValueType();}
163
167 template <class ValueType>
169
178 PyObject* getAttribute(AttributeType type, std::string opalName) const;
179
187 void setAttribute(AttributeType type, std::string opalName, PyObject* value);
188
190 std::shared_ptr<C> getOpalShared() {return object_m;}
191
192protected:
193 static std::vector<AttributeDef> attributes;
194 static std::string classDocstring;
196 std::shared_ptr<C> object_m;
199 std::string getDocString(AttributeDef& def);
200 static void execute(PyOpalObject<C>& pyobject);
201 static void registerObject(PyOpalObject<C>& pyobject);
202 static boost::python::object getPyOpalElement(PyOpalObject<C>& pyobject);
203};
204
205template <class C>
206boost::python::object getFieldValue(PyOpalObjectNS::PyOpalObject<C>& pyobject, double x, double y, double z, double t) {
207 std::shared_ptr<C> objectPtr = pyobject.getOpalShared();
208 objectPtr->update();
209 ElementBase* element = objectPtr->getElement()->removeWrappers();
210 Component* component = dynamic_cast<Component*>(element);
211 if (component == NULL) {
212 throw OpalException("PyElement<C>::getFieldValue",
213 "Failed to deduce Component from ElementBase.");
214 }
215
216 Vector_t R(x, y, z);
217 Vector_t P(0.0, 0.0, 0.0);
218 Vector_t B;
219 Vector_t E;
220 bool outOfBounds = component->apply(R, P, t, E, B);
221 return boost::python::make_tuple(outOfBounds,
222 B[0], B[1], B[2],
223 E[0], E[1], E[2]);
224}
225
226
227
228
229
231template <class C>
232struct PyOpalObjectGetProperty : boost::python::default_call_policies {
233public:
238 PyOpalObjectGetProperty(AttributeType type, std::string opalName): type_m(type), opalName_m(opalName) {}
241
249 template <class ArgumentPackage>
250 PyObject* postcall(ArgumentPackage const&, PyObject* result);
251
253 static void setObject(const PyOpalObject<C>* object) {object_m = object;}
254
255private:
257 std::string opalName_m;
259};
260
262template <class C>
263struct PyOpalObjectSetProperty : boost::python::default_call_policies {
264public:
269 PyOpalObjectSetProperty(AttributeType type, std::string opalName): type_m(type), opalName_m(opalName) {}
272
280 template <class ArgumentPackage>
281 PyObject* postcall(ArgumentPackage const& args, PyObject* result);
282
284 static void setObject(PyOpalObject<C>* object) {object_m = object;}
285
286private:
288 std::string opalName_m;
290};
291
293
294
295template <class C>
297 std::shared_ptr<C> objectPtr = pyobject.getOpalShared();
298 objectPtr->execute();
299}
300
301template <class C>
303 C* wrappedC = pyobject.getOpalShared().get();
304 Object* objectPtr = dynamic_cast<Object*>(wrappedC);
305 if (objectPtr == NULL) {
306 throw OpalException("PyOpalObject<C>::registerObject",
307 "Trying to register something that was not a Opal Object");
308 }
309 //Object* objectPtr = &(*pyobject.getOpalShared());
310 OpalData::getInstance()->define(objectPtr);
311}
312
313
314template <class C>
316 std::shared_ptr<OpalElement> elementPtr =
317 std::dynamic_pointer_cast<OpalElement, C>(pyobject.getOpalShared());
318 if (elementPtr.get() == NULL) {
319 throw OpalException("PyOpalObject<C>::getPyOpalElement",
320 "Wrapped object was not an OpalElement");
321 }
322 PyOpalObject<OpalElement> element(elementPtr);
323 boost::python::object pyelement(element);
324 return pyelement;
325}
326
327
328// defined in PyOpalElement
329template <>
331
332template <class C>
333PyOpalObject<C>::PyOpalObject() : object_m(new C) {}
334
335template <class C>
336PyObject* PyOpalObject<C>::getAttribute(AttributeType type, std::string opalName) const {
337 if (!object_m) {
338 throw OpalException("PyOpalObject<C>::getRealAttribute",
339 "Object was not initialised");
340 }
341 Attribute* attribute = object_m->findAttribute(opalName);
342 if (attribute == NULL) {
343 throw OpalException("PyOpalObject<C>::getRealAttribute",
344 "Failed to parse attribute "+opalName);
345 }
346 PyObject* pyvalue;
347 // I spent quite a bit of time trying to get this type unwinding to work
348 // using templates (so it is done at compile time). In the end I couldn't
349 // fight the template syntax and had to do it at runtime using an enum; it's
350 // not so bad - the memory footprint is smaller and, tbh, template syntax is
351 // horrible so might be easier to use.
352 if (type == DOUBLE) {
353 double value = Attributes::getReal(*attribute);
354 pyvalue = PyFloat_FromDouble(value);
355 } else if (type == INT) {
356 double value = Attributes::getReal(*attribute);
357 pyvalue = PyLong_FromDouble(value);
358 } else if (type == STRING) {
359 std::string value = Attributes::getString(*attribute);
360 pyvalue = PyUnicode_FromString(value.c_str());
361 } else if (type == BOOL) {
362 bool value = Attributes::getBool(*attribute);
363 if (value) {
364 pyvalue = Py_True;
365 } else {
366 pyvalue = Py_False;
367 }
368 } else if (type == FLOATLIST) {
369 std::vector<double> value = Attributes::getRealArray(*attribute);
370 pyvalue = PyList_New(value.size());
371 for (size_t i = 0; i < value.size(); ++i) {
372 PyList_SetItem(pyvalue, i, PyFloat_FromDouble(value[i])); // WARNING check memory...
373 }
374 } else {
375 throw OpalException("PyOpalObject<C>::getAttribute",
376 "Attribute type "+attributeName[type]+" not implemented");
377 }
378 Py_INCREF(pyvalue);
379 return pyvalue;
380}
381
382
383template <class C>
384void PyOpalObject<C>::setAttribute(AttributeType type, std::string opalName, PyObject* pyvalue) {
385 if (!object_m) {
386 throw OpalException("PyOpalObject<C>::setAttribute",
387 "Element was not initialised");
388 }
389 Attribute* attribute = object_m->findAttribute(opalName);
390 if (attribute == NULL) {
391 throw OpalException("PyOpalObject<C>::setAttribute",
392 "Failed to parse attribute "+opalName);
393 }
394 if (type == DOUBLE) {
395 double value = PyFloat_AsDouble(pyvalue);
396 Attributes::setReal(*attribute, value);
397 } else if (type == INT) {
398 double value = PyLong_AsDouble(pyvalue);
399 Attributes::setReal(*attribute, value);
400 } else if (type == STRING) {
401 std::string value = PyUnicode_AsUTF8(pyvalue);
402 Attributes::setString(*attribute, value);
403 } else if (type == BOOL) {
404 bool value = PyObject_IsTrue(pyvalue);
405 Attributes::setBool(*attribute, value);
406 } else if (type == FLOATLIST) {
407 Py_ssize_t listSize = PyList_Size(pyvalue);
408 std::vector<double> value(listSize);
409 for (Py_ssize_t i = 0; i < listSize; ++i) {
410 double value_i = PyFloat_AsDouble(PyList_GetItem(pyvalue, i));
411 value[i] = value_i;
412 }
413 Attributes::setRealArray(*attribute, value);
414 } else {
415 throw OpalException("PyOpalObject<C>::setAttribute",
416 "Attribute type "+attributeName[type]+" not implemented");
417 }
418}
419
420template <class C>
421PyOpalObject<C>::PyOpalObject(const PyOpalObject<C>& rhs) : object_m(rhs.object_m) {
422}
423
424template <class C>
426 Attribute* attribute = object_m->findAttribute(def.opalName_m);
427 if (attribute == NULL) {
428 throw OpalException("PyOpalObject<C>::getRealAttribute",
429 "Failed to parse attribute "+def.opalName_m);
430 }
431 std::string docString = def.pyName_m+" ("+attributeName[def.type_m]+"): "+attribute->getHelp();
432 if (def.docString_m != "") {
433 docString = def.pyName_m+" ("+attributeName[def.type_m]+"): "+def.docString_m;
434 }
435 return docString;
436}
437
438template <class C>
439boost::python::class_<PyOpalObject<C> > PyOpalObject<C>::make_class(const char* className) {
440 typedef boost::python::class_<PyOpalObject<C> > PyClass;
441 boost::python::docstring_options docop(true, true, false); // user_def, py_sig, cpp_sig
442 PyClass pyclass = PyClass(className);
443 addAttributes(pyclass);
444 return pyclass;
445}
446
447
448template <class C>
449template <class PYCLASS>
450void PyOpalObject<C>::addExecute(PYCLASS& pyclass) {
451 pyclass.def("execute", &PyOpalObject<C>::execute);
452}
453
454template <class C>
455template <class PYCLASS>
456void PyOpalObject<C>::addRegister(PYCLASS& pyclass) {
457 pyclass.def("register", &PyOpalObject<C>::registerObject);
458}
459
460template <class C>
461template <class PYCLASS>
463 pyclass.def("get_opal_element", &PyOpalObject<C>::getPyOpalElement);
464}
465
466
467
468template <class C>
469template <class PYCLASS>
470void PyOpalObject<C>::addAttributes(PYCLASS& pyclass) {
471 for (std::vector<AttributeDef>::iterator iter = attributes.begin(); iter != attributes.end(); ++iter) {
472 PyOpalObjectGetProperty<C> getProp(iter->type_m, iter->opalName_m);
473 PyOpalObjectSetProperty<C> setProp(iter->type_m, iter->opalName_m);
474 std::string docString = getDocString(*iter);
475 std::string pyname = iter->pyName_m.c_str();
476 if (iter->type_m == DOUBLE) {
477 pyclass.add_property(pyname.c_str(),
478 boost::python::make_function(&PyC::dummyGet<double>, getProp),
479 boost::python::make_function(&PyC::dummySet<double>, setProp),
480 docString.c_str()
481 );
482 } else if (iter->type_m == INT) {
483 pyclass.add_property(pyname.c_str(),
484 boost::python::make_function(&PyC::dummyGet<int>, getProp),
485 boost::python::make_function(&PyC::dummySet<int>, setProp),
486 docString.c_str()
487 );
488 } else if (iter->type_m == STRING) {
489 pyclass.add_property(pyname.c_str(),
490 boost::python::make_function(&PyC::dummyGet<std::string>, getProp),
491 boost::python::make_function(&PyC::dummySet<std::string>, setProp),
492 docString.c_str()
493 );
494 } else if (iter->type_m == BOOL) {
495 pyclass.add_property(pyname.c_str(),
496 boost::python::make_function(&PyC::dummyGet<bool>, getProp),
497 boost::python::make_function(&PyC::dummySet<bool>, setProp),
498 docString.c_str()
499 );
500 } else if (iter->type_m == FLOATLIST) {
501 pyclass.add_property(pyname.c_str(),
502 boost::python::make_function(&PyC::dummyGet<boost::python::list>, getProp),
503 boost::python::make_function(&PyC::dummySet<boost::python::list>, setProp),
504 docString.c_str()
505 );
506 } else {
507 // Looks like exception handling doesn't work properly at module
508 // import time so this may not be handled politely - thrown as an
509 // unrecognised SystemError
510 throw OpalException("PyOpalObject<C>::addAttributes", "Type not implemented");
511 }
512 }
513}
514
515
517
518template <class C>
519template <class ArgumentPackage>
520PyObject* PyOpalObjectGetProperty<C>::postcall(ArgumentPackage const&, PyObject* result) {
521 Py_DECREF(result);
522 result = object_m->getAttribute(type_m, opalName_m);
523 return result;
524}
525
526template <class C>
528
530
531template <class C>
532template <class ArgumentPackage>
533PyObject* PyOpalObjectSetProperty<C>::postcall(ArgumentPackage const& args, PyObject* result) {
534 PyObject* value;
535 PyObject* pyObject; // this is a direct pointer to the C but I don't know how to unwrap it...
536 if (!PyArg_ParseTuple(args, "OO", &pyObject, &value)) {
537 return NULL; // ParseTuple sets the error message
538 }
539 Py_DECREF(result);
540 object_m->setAttribute(type_m, opalName_m, value);
541 object_m = NULL;
542 Py_RETURN_NONE;
543}
544
545template <class C>
547
548} // PyOpalObject
549} // PyOpal
550
551#endif // PyOpalObject_H
boost::python::object getFieldValue(PyOpalObjectNS::PyOpalObject< C > &pyobject, double x, double y, double z, double t)
Definition: PyOpalObject.h:206
std::map< AttributeType, std::string > attributeName
Definition: PyOpalObject.cpp:7
double getReal(const Attribute &attr)
Return real value.
Definition: Attributes.cpp:252
void setRealArray(Attribute &attr, const std::vector< double > &value)
Set array value.
Definition: Attributes.cpp:309
void setBool(Attribute &attr, bool val)
Set logical value.
Definition: Attributes.cpp:119
void setString(Attribute &attr, const std::string &val)
Set string value.
Definition: Attributes.cpp:391
bool getBool(const Attribute &attr)
Return logical value.
Definition: Attributes.cpp:100
void setReal(Attribute &attr, double val)
Set real value.
Definition: Attributes.cpp:271
std::vector< double > getRealArray(const Attribute &attr)
Get array value.
Definition: Attributes.cpp:294
std::string getString(const Attribute &attr)
Get string value.
Definition: Attributes.cpp:343
std::string::iterator iterator
Definition: MSLang.h:16
boost::function< boost::tuple< double, bool >(arguments_t)> type
Definition: function.hpp:21
void addRegister(PYCLASS &pyclass)
Definition: PyOpalObject.h:456
static void registerObject(PyOpalObject< C > &pyobject)
Definition: PyOpalObject.h:302
static std::vector< AttributeDef > attributes
Definition: PyOpalObject.h:193
void addAttributes(PYCLASS &pyclass)
Definition: PyOpalObject.h:470
void addGetOpalElement(PYCLASS &pyclass)
Definition: PyOpalObject.h:462
std::string getDocString(AttributeDef &def)
Definition: PyOpalObject.h:425
PyOpalObject(std::shared_ptr< C > object)
Definition: PyOpalObject.h:124
static boost::python::object getPyOpalElement(PyOpalObject< C > &pyobject)
Definition: PyOpalObject.h:315
std::shared_ptr< C > getOpalShared()
Definition: PyOpalObject.h:190
PyObject * getAttribute(AttributeType type, std::string opalName) const
Definition: PyOpalObject.h:336
boost::python::class_< PyC > make_class(const char *className)
Definition: PyOpalObject.h:439
static void execute(PyOpalObject< C > &pyobject)
Definition: PyOpalObject.h:296
void addExecute(PYCLASS &pyclass)
Definition: PyOpalObject.h:450
void setAttribute(AttributeType type, std::string opalName, PyObject *value)
Definition: PyOpalObject.h:384
static void setObject(const PyOpalObject< C > *object)
Definition: PyOpalObject.h:253
static const PyOpalObject< C > * object_m
Definition: PyOpalObject.h:258
PyObject * postcall(ArgumentPackage const &, PyObject *result)
Definition: PyOpalObject.h:520
PyOpalObjectGetProperty(AttributeType type, std::string opalName)
Definition: PyOpalObject.h:238
static void setObject(PyOpalObject< C > *object)
Definition: PyOpalObject.h:284
PyObject * postcall(ArgumentPackage const &args, PyObject *result)
Definition: PyOpalObject.h:533
PyOpalObjectSetProperty(AttributeType type, std::string opalName)
Definition: PyOpalObject.h:269
A representation of an Object attribute.
Definition: Attribute.h:52
const std::string & getHelp() const
Return the help string.
Definition: Attribute.cpp:82
The base class for all OPAL objects.
Definition: Object.h:48
static OpalData * getInstance()
Definition: OpalData.cpp:196
void define(Object *newObject)
Define a new object.
Definition: OpalData.cpp:489
Interface for a single beam element.
Definition: Component.h:50
virtual bool apply(const size_t &i, const double &t, Vector_t &E, Vector_t &B)
Definition: Component.cpp:99
The base class for all OPAL exceptions.
Definition: OpalException.h:28