Python wrapping of DataStructure

Files structures

The definition of exposed methods of the DataStructure objects is included into the libaster.so. The main file is bibcxx/PythonBindings/LibAster.cxx that exports all symbols.

By convention, one file is created per derivated DataStructure object named <DataStructure object>Interface.{h,cxx}.

Exported methods

Constructors

DataStructure objects must be wrapped by a std::shared_ptr.

The definition of the wrapper must reflect the inheritance of the underlying C++ DataStructures. In the following example, Function is derivated from BaseFunction. It is necessary to pass a Python Function object where a generic BaseFunction is expected.

Note

The constructors of the underlying instance should not be available to the final user. That’s why the definition should use py::no_init.

To allow the serialization of the DataStructure objects using the Python pickling mechanism, we need a constructor that accepts the Jeveux name. The final user must not call this constructor.

For the creation of the DataStructure from Python, we also need a default constructor (with or without any argument).

Note

These constructors are defined using factory functions by templating initFactoryPtr.

Examples

In this simple example one defines a default constructor (without argument) and the constructor used during unpickling that accepts the Jeveux name:

void exportFunctionToPython( py::module_ &mod )
{
    namespace py = pybind11;
    ...

    py::class_< Function, Function::FunctionPtr, BaseFunction > (
        mod, "Function" )
        .def( "__init__", py::make_constructor(
            &initFactoryPtr< Function >) )
        .def( "__init__", py::make_constructor(
            &initFactoryPtr< Function,
                             std::string >) )
        .def( "setValues", &Function::setValues )
        ...
    ;
}

A more complex example where the constructor needs another object. So the default constructor needs a pointer on a Model and another that takes the Jeveux name and this pointer:

void exportElementaryCharacteristicsToPython( py::module_ &mod )
{
    using namespace pybind11;

    class_< ElementaryCharacteristics, ElementaryCharacteristics::ElementaryCharacteristicsPtr,
            bases< DataStructure > > ( "ElementaryCharacteristics", no_init )
        .def( "__init__", make_constructor(
            &initFactoryPtr< ElementaryCharacteristics,
                             ModelPtr >) )
        .def( "__init__", make_constructor(
            &initFactoryPtr< ElementaryCharacteristics,
                             std::string,
                             ModelPtr >) )
    ;
};

Other methods

See Recommendations / howto for methods with default arguments.

Pickling support

See code_aster.Supervis.Serializer module for the serialization management.

  • DataStructures usually delegate the serialization to their Python objects.

  • Constructors arguments defined by __getinitargs__() implemented in code_aster.Objects.DataStructure for most of the classes.

    Example: ElementaryCharacteristics defines its own arguments. __getinitargs__() returns a tuple with two elements: the Jeveux object name and the Model that are passed to the constructor.

  • To restore the internal state of the object, subclasses should defined their own __getstate__() and __setstate__() methods. This is done through a subclass of InternalStateBuilder.

    Example: AssemblyMatrix does not take its DOFNumbering as argument in its constructor. So it is stored by the save() method of its InternalStateBuilder and restored by the restore() method.