OPAL (Object Oriented Parallel Accelerator Library) 2022.1
OPAL
PyElement.h
Go to the documentation of this file.
1#ifndef PyElement_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 PyElementNS {
48
49// forward declarations
50template <class C> class PyElement;
51template <class C> struct PyElementGetProperty;
52template <class C> struct PyElementSetProperty;
53
61
63extern std::map<AttributeType, std::string> attributeName; // defined in PyElement.cpp
64
74 std::string opalName_m;
75 std::string pyName_m;
76 std::string docString_m;
78 // could also add a "read only" flag as some attributes are read only
79};
80
118template <class C>
120public:
121 typedef PyElement<C> PyC; // for convenience
123 inline PyElement();
125 PyElement(std::shared_ptr<C> element) : element_m(element) {}
127 PyElement(const PyElement<C>& rhs);
130
134 boost::python::class_<PyC> make_class(const char* className);
135
138 void addAttributes(boost::python::class_<PyC>& pyclass);
139
140
144 template <class ValueType>
145 ValueType dummyGet() const {PyElementGetProperty<C>::setElement(this); return 0;}
146
150 template <class ValueType>
151 void dummySet(ValueType test) {PyElementSetProperty<C>::setElement(this);}
152
161 PyObject* getAttribute(AttributeType type, std::string opalName) const;
162
170 void setAttribute(AttributeType type, std::string opalName, PyObject* value);
171
181 boost::python::object getFieldValue(double x, double y, double z, double t) const;
182
184 boost::python::object getPyOpalElement();
185
188
189 C* getElement() {return element_m.get();}
190
191protected:
192 static std::vector<AttributeDef> attributes;
193 static std::string classDocstring;
195 static bool hasGetFieldValue;
196 std::shared_ptr<C> element_m;
199 std::string getDocString(AttributeDef& def);
200};
201
203template <class C>
204struct PyElementGetProperty : boost::python::default_call_policies {
205public:
210 PyElementGetProperty(AttributeType type, std::string opalName): type_m(type), opalName_m(opalName) {}
213
221 template <class ArgumentPackage>
222 PyObject* postcall(ArgumentPackage const&, PyObject* result);
223
225 static void setElement(const PyElement<C>* element) {element_m = element;}
226
227private:
229 std::string opalName_m;
230 static const PyElement<C>* element_m;
231};
232
234template <class C>
235struct PyElementSetProperty : boost::python::default_call_policies {
236public:
241 PyElementSetProperty(AttributeType type, std::string opalName): type_m(type), opalName_m(opalName) {}
244
252 template <class ArgumentPackage>
253 PyObject* postcall(ArgumentPackage const& args, PyObject* result);
254
256 static void setElement(PyElement<C>* element) {element_m = element;}
257
258private:
260 std::string opalName_m;
262};
263
265
266// defined in PyOpalElement
267template <>
269
270template <class C>
271PyElement<C>::PyElement() : element_m(new C) {}
272
273template <class C>
275
276template <class C>
277boost::python::object PyElement<C>::getFieldValue(double x, double y, double z, double t) const {
278 Vector_t R(x, y, z);
279 Vector_t P(0.0, 0.0, 0.0);
280 Vector_t B;
281 Vector_t E;
282 element_m->update();
283 ElementBase* element = element_m->getElement()->removeWrappers();
284 Component* component = dynamic_cast<Component*>(element);
285 if (component == NULL) {
286 throw OpalException("PyElement<C>::getFieldValue",
287 "Failed to deduce Component from ElementBase.");
288 }
289 bool outOfBounds = component->apply(R, P, t, E, B);
290 return boost::python::make_tuple(outOfBounds,
291 B[0], B[1], B[2],
292 E[0], E[1], E[2]);
293}
294
295template <class C>
296PyObject* PyElement<C>::getAttribute(AttributeType type, std::string opalName) const {
297 if (!element_m) {
298 throw OpalException("PyElement<C>::getRealAttribute",
299 "Element was not initialised");
300 }
301 Attribute* attribute = element_m->findAttribute(opalName);
302 if (attribute == NULL) {
303 throw OpalException("PyElement<C>::getRealAttribute",
304 "Failed to parse attribute "+opalName);
305 }
306 PyObject* pyvalue;
307 // I spent quite a bit of time trying to get this type unwinding to work
308 // using templates (so it is done at compile time). In the end I couldn't
309 // fight the template syntax and had to do it at runtime using an enum; it's
310 // not so bad - the memory footprint is smaller and, tbh, template syntax is
311 // horrible so might be easier to use.
312 if (type == DOUBLE) {
313 double value = Attributes::getReal(*attribute);
314 pyvalue = PyFloat_FromDouble(value);
315 } else if (type == INT) {
316 double value = Attributes::getReal(*attribute);
317 pyvalue = PyLong_FromDouble(value);
318 }
319 Py_INCREF(pyvalue);
320 return pyvalue;
321}
322
323
324template <class C>
325void PyElement<C>::setAttribute(AttributeType type, std::string opalName, PyObject* pyvalue) {
326 if (!element_m) {
327 throw OpalException("PyElement<C>::getRealAttribute",
328 "Element was not initialised");
329 }
330 Attribute* attribute = element_m->findAttribute(opalName);
331 if (attribute == NULL) {
332 throw OpalException("PyElement<C>::getRealAttribute",
333 "Failed to parse attribute "+opalName);
334 }
335 if (type == DOUBLE) {
336 double value = PyFloat_AsDouble(pyvalue);
337 Attributes::setReal(*attribute, value);
338 } else if (type == INT) {
339 double value = PyLong_AsDouble(pyvalue);
340 Attributes::setReal(*attribute, value);
341 }
342 return;
343}
344
345template <class C>
346PyElement<C>::PyElement(const PyElement<C>& rhs) : element_m(rhs.element_m) {
347}
348
349template <class C>
351 Attribute* attribute = element_m->findAttribute(def.opalName_m);
352 if (attribute == NULL) {
353 throw OpalException("PyElement<C>::getRealAttribute",
354 "Failed to parse attribute "+def.opalName_m);
355 }
356 std::string docString = def.pyName_m+" ("+attributeName[def.type_m]+"): "+attribute->getHelp();
357 if (def.docString_m != "") {
358 docString = def.pyName_m+" ("+attributeName[def.type_m]+"): "+def.docString_m;
359 }
360 return docString;
361}
362
363template <class C>
364boost::python::class_<PyElement<C> > PyElement<C>::make_class(const char* className) {
365 boost::python::docstring_options docop(true, true, false); // user_def, py_sig, cpp_sig
366 auto pyclass = boost::python::class_<PyC>(className);
367 addAttributes(pyclass);
368 return pyclass;
369}
370
371template <class C>
372void PyElement<C>::addAttributes(boost::python::class_<PyElement<C> >& pyclass) {
373 for (std::vector<AttributeDef>::iterator iter = attributes.begin(); iter != attributes.end(); ++iter) {
374 PyElementGetProperty<C> getProp(iter->type_m, iter->opalName_m);
375 PyElementSetProperty<C> setProp(iter->type_m, iter->opalName_m);
376 std::string docString = getDocString(*iter);
377 std::string pyname = iter->pyName_m.c_str();
378 if (iter->type_m == DOUBLE) {
379 pyclass.add_property(pyname.c_str(),
380 boost::python::make_function(&PyC::dummyGet<double>, getProp),
381 boost::python::make_function(&PyC::dummySet<double>, setProp),
382 docString.c_str()
383 );
384 } else if (iter->type_m == INT) {
385 pyclass.add_property(pyname.c_str(),
386 boost::python::make_function(&PyC::dummyGet<int>, getProp),
387 boost::python::make_function(&PyC::dummySet<int>, setProp),
388 docString.c_str()
389 );
390 }
391 }
392 pyclass.def("get_opal_element", &PyC::getPyOpalElement);
393 if (hasGetFieldValue) {
394 pyclass.def("get_field_value", &PyC::getFieldValue);
395 }
396}
397
398
399template <class C>
401 OpalElement* element = dynamic_cast<OpalElement*>(element_m.get());
402 if (element == NULL) {
403 throw OpalException("PyElement<C>::getOpalElement",
404 "Failed to cast to OpalElement");
405 }
406 return element;
407}
408
409template <class C>
410boost::python::object PyElement<C>::getPyOpalElement() {
411 std::shared_ptr<OpalElement> elementPtr = std::dynamic_pointer_cast<OpalElement, C>(element_m);
412 if (elementPtr.get() == NULL) {
413 throw OpalException("PyElement<C>::getPyOpalElement", "Wrapped object was not an OpalElement");
414 }
415 PyElement<OpalElement> element(elementPtr);
416 boost::python::object pyelement(element);
417 return pyelement;
418}
419
421
422template <class C>
423template <class ArgumentPackage>
424PyObject* PyElementGetProperty<C>::postcall(ArgumentPackage const&, PyObject* result) {
425 Py_DECREF(result);
426 result = element_m->getAttribute(DOUBLE, opalName_m);
427 return result;
428}
429
430template <class C>
432
434
435template <class C>
436template <class ArgumentPackage>
437PyObject* PyElementSetProperty<C>::postcall(ArgumentPackage const& args, PyObject* result) {
438 PyObject* value;
439 PyObject* pyElement; // this is a direct pointer to the C but I don't know how to unwrap it...
440 if (!PyArg_ParseTuple(args, "OO", &pyElement, &value)) {
441 return NULL; // ParseTuple sets the error message
442 }
443 Py_DECREF(result);
444 element_m->setAttribute(DOUBLE, opalName_m, value);
445 element_m = NULL;
446 Py_RETURN_NONE;
447}
448
449template <class C>
451
452} // PyElementNS
453} // PyOpal
454
455#endif // PyElement_H
std::map< AttributeType, std::string > attributeName
Definition: PyElement.cpp:7
boost::python::object getFieldValue(PyOpalObjectNS::PyOpalObject< C > &pyobject, double x, double y, double z, double t)
Definition: PyOpalObject.h:206
double getReal(const Attribute &attr)
Return real value.
Definition: Attributes.cpp:252
void setReal(Attribute &attr, double val)
Set real value.
Definition: Attributes.cpp:271
std::string::iterator iterator
Definition: MSLang.h:16
boost::function< boost::tuple< double, bool >(arguments_t)> type
Definition: function.hpp:21
PyElement(std::shared_ptr< C > element)
Definition: PyElement.h:125
static std::string classDocstring
Definition: PyElement.h:193
void dummySet(ValueType test)
Definition: PyElement.h:151
PyObject * getAttribute(AttributeType type, std::string opalName) const
Definition: PyElement.h:296
static std::vector< AttributeDef > attributes
Definition: PyElement.h:192
std::shared_ptr< C > element_m
Definition: PyElement.h:196
ValueType dummyGet() const
Definition: PyElement.h:145
std::string getDocString(AttributeDef &def)
Definition: PyElement.h:350
void setAttribute(AttributeType type, std::string opalName, PyObject *value)
Definition: PyElement.h:325
OpalElement * getOpalElement()
Definition: PyElement.h:400
boost::python::object getFieldValue(double x, double y, double z, double t) const
Definition: PyElement.h:277
boost::python::class_< PyC > make_class(const char *className)
Definition: PyElement.h:364
void addAttributes(boost::python::class_< PyC > &pyclass)
Definition: PyElement.h:372
boost::python::object getPyOpalElement()
Definition: PyElement.h:410
PyObject * postcall(ArgumentPackage const &, PyObject *result)
Definition: PyElement.h:424
static const PyElement< C > * element_m
Definition: PyElement.h:230
static void setElement(const PyElement< C > *element)
Definition: PyElement.h:225
PyElementGetProperty(AttributeType type, std::string opalName)
Definition: PyElement.h:210
PyElementSetProperty(AttributeType type, std::string opalName)
Definition: PyElement.h:241
PyObject * postcall(ArgumentPackage const &args, PyObject *result)
Definition: PyElement.h:437
static void setElement(PyElement< C > *element)
Definition: PyElement.h:256
static PyElement< C > * element_m
Definition: PyElement.h:261
A representation of an Object attribute.
Definition: Attribute.h:52
const std::string & getHelp() const
Return the help string.
Definition: Attribute.cpp:82
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