Package mbdyn
[hide private]

Package mbdyn

source code

This package is based on the top of MBDyn, a MultiBody Dynamics software written at the university Politecnico di Milano. In MBDyn, a simulation is started by providing an input file describing reference frames, nodes and elements. This package motivation is to turn MBDyn into a multibody dynamics library for Python, so then the user communicates with objects during the whole simulation. He does not describe its problem by a MBDyn input file any more. But moreover he can interact with objects at run time.

Such project requires some vector and matrix manipulation, that will be provided by the Numpy package. To plot the results, the Matplotlib package is suggested (and used during some examples) but not needed for running a simulation. The communication with MBDyn is achieved and documented in the mbdyn.bindings package. The next section is an overview about the MBDyn way of work and why the development was started around it. The second section explains how Python was used for getting new functionalities. What are the current achievements?

1 MBDyn description

MBDyn can also be defined as a multi physics application, with an aerodynamic, electric, hydraulic and structural library. In this package, the work has been limited to the structural one because the software was used in mechanical engineering. The same approach could however been used for the others fields. Before explaining the reasons for developing new functionalities, the MBDyn software in its current version deserves many credits. But the second section will expose the problems encountered.

1.1 Thanks for this public service

The first credit is surely the gathering of a multibody dynamics community under a public software. Realising the code under the General Public License (GPL) has opened innovative and exhilarating development opportunities that a strategic patenting approach would have completely denied. This point of view completely fits the author status, that has developed this project as a part of a Master thesis. All the others credits are to inherit from a significant work for simulating multibody dynamics problem. Each library has been developed by being based on three items: reference frames, nodes and elements. The reference frames are only used to describe the problem, they will not be part of the integration, but they allow to set the initial conditions on nodes and elements. The nodes are the problem unknowns, they introduce degrees of freedom. In structural mechanics (it was the only library studied so far), each node represent 6 degrees of freedom: 3 translations and 3 rotations. For solving the problem, those degrees of freedom need to get constraints, so then there are as much equations as unknowns, and moreover the equations need to be described. Those two roles are met by elements as a consequence attached to nodes. The elements available in this package are bodies, for introducing mass and inertia quantities, forces, for supplying external works, joints, for setting constraints and beams, for describing flexible multibody problems. That approach deserves many credits because the usual problems to start an integration from scratch have been encapsulated into an interface. The theoretical part of MBDyn is described in many publications, the main one used during the development was the Phd thesis of Pierangelo Masarati. The software is mature for mechanical engineering. Why was it needed to develop functionalities?

1.2 Communication problematic with objects

As described, MBDyn allows a problem description by abstract items (reference frames, nodes and elements) written in an input file, but the user will certainly deal with a specific model. Thus it could be convenient to describe meaningful objects for the problem, manipulating the abstract ones. The following section will introduce this problematic more in details. Moreover once the MBDyn process starts, an interaction with its objects is not available as an interface, but requires C++ development of the sources. This part is however addressed in the mbdyn.bindings module.

User requirements

By using a general description with nodes and elements, almost all the quantities computed were output in text files. Just some of the objects could write optional results, like for example dynamic structural nodes, but it could not be specified for every integrated quantity. It sounds really optional. Why does the user need such feature? Because he knows its model and useless operations at run time are critical as many iterations are involved. Some of the items are used only for the model building but will not output any interesting results. A variant would be to perform different simulations for the same model, a particular behavior being investigated each time. For example, a node under constraints does not need to output all its degrees of freedom, some of them will inherently be out of interest. If the user just want to know about the position of a node, there is no need to output the rotation matrix, velocity and angular velocity. C++ is fast but why does it do many useless operations? The reason comes from the input file. The user has already to supply a value for every field. If moreover he has to specify each quantity to save, or may want a particular degree of freedom, the parameters to write are too numerous.

Forcing the user to supply every field is furthermore relevant for a beginner, but not so convenient for an end user. For example for every node, the user has to write the position, rotation matrix, velocity and angular velocity relative to a reference frame. But a node is supposed to inherit from the reference frame attributes. If those last ones already described the node, the values entered will be a null position, velocity and angular velocity, the rotation matrix will be the identity one. Most of the nodes will be like that because the user needs to set up reference frames for defining the initial conditions of a complex problem. Generally just one or two parameters are specified differently between a node and its reference frame. As a result, it could be convenient to have default value for every item (reference frame, node or element). It could also be useful to describe a custom item having default values suited for the simulation.

The user may also need his own custom class based on an available one. The advantage is that he will then instantiate the same class in his script, that will avoid him to repeat the same description on the chosen library item. This is clearly a problem that developers can not guess, the goal is only to write a library for solving multibody problems. A suited template for a specific problem will be the user job that could as an alternative develop a corresponding library. Two requirements seem to be needed for the user: he would like to specify optional behaviors for some objects and by using the existing ones, he may need to describe its own class. Could it be easy for the developers?

Developer difficulties

The problem is also on the developer side. Parsing a text file does not really allow to have a field omitted. In the parser source code, there is a number of keywords expected for each item. By a series of tests, all the needed values are retrieved from string to C++ data types. Hence the instance is created. This method is efficient to find a syntax error or avoid the user to build an unexpected object. Let's now examine the problem of the optional fields. Extra tests will have to be added. But in case the number of parameters varies too much between a minimal and complete definition, it will be difficult to catch the syntax error. Was it the main keyword or an option expected? Moreover when the parsing occurs, the instance is not yet created because the values for initializing the constructor are still processed. Then it would not be convenient to have many optional fields in the constructor. But as an alternative as soon as the instance created, methods will be applied on it for setting the optional fields. An advantage of object oriented programming, dealing with an object without knowing its implementation, is lost because this is the developer that set options on an object that he knows. With a text file, there is furthermore no way that the user describe its own class from the existing ones. He has to dive in the C++ code. The discussion seems to be similar as in the mbdyn.bindings module, the MBDyn design does not expose its objects structure to the user as a library does. But would not it be simpler for both users and developers?

Conclusion

To resume MBDyn is a great base for mechanical engineering, and simulation of multibody problems in general. The user inherits from a work done by expert researchers and publicly available. This is the gathering of those two points that really gives value to MBDyn. The limitations encountered were however in the communication part with the library, that can not be easily used to its full value. Hence Python is coming.

2 Providing new functionalities

The communication problem with MBDyn objects at run time is fully addressed in mbdyn.bindings. Thus by filling an input file to WraptMBDyn, the MBDyn integration can be controlled from Python. The next step is to write an interface to every MBDyn object. The first benefit will be to build a model by using the language features of Python. Then as the user deals with objects, it becomes easier to describe each item role than in a MBDyn input file. Finally the simulation results will be set as object attributes, facilitating the post processing.

2.1 Building models from Python

The first point to highlight, is that the situation is simpler than in the mbdyn.bindings module for the reason that the MBDyn service is not run. In the pre processing part, the task is finally to write a text file from Python in an object oriented way. This input file will start WraptMBDyn and once the interaction finished, the post processing is also done without a running MBDyn. This point makes the development simpler because it can always been done in Python. Thus there is not all the work of compiling a C++ shared library and SWIG extension with SCons , debugging memory allocation problems with GDB and so on. From here, how was Python used?

Python was used to describe an interface to every MBDyn object. The first advantage was the syntax (for example a class or function is defined by one keyword) and language features (list, dictionaries, everything object, third part packages). The crucial point is that Python allows references to objects. It would have been difficult, and also surely obscure, to write an interface to a C++ code in a language that does not support this feature because the MBDyn design uses it heavily. The disadvantage is for the user coming from a language that never deals with references, that is to say Matlab. In that case the language will of course be simpler (so commercial advertisements are more attractive) but also very limited. On that point Python does well because it only simplifies the pointer mechanism. For the user this is however very important to understand that once he has created an instance , he will always manipulate references to it. This is where the interface really starts to make sense. By using this package, the user first describes an object suited for his problem. Then only the MBDyn ones are going to be simulated but at each time step, in his script, he will have references to the objects current status and can use them to solve its specific problem. All the Python libraries and services are moreover available.

A level of abstraction becomes possible, in the sense that the user writes a suited object, handling the MBDyn results that he wants. Then he can dialog with this new object, solving only a small part of the problem, and forgets about the MBDyn abstract objects (reference frames, nodes and elements). By connecting different small pieces together, the whole problem is getting solved. Moreover useful parts can be shared by different simulations by writing a specific module. Examples of this approach are used in the wind-sim-suite project. The next section explains in details the pre-processing work with Python objects.

2.2 Pre-processing

During the pre-processing part, the main progress is the possibility to deal with objects instead of having to write a MBDyn input file. Then how is this input file written? If the simulation is now started by a Python script, it should also be easier to describe each object role for both users and developers. The next paragraphs expose the package organisation on those points.

Input file writing

Each object is responsible of writing its part of the input file. Those informations finally represent how MBDyn will instantiate the C++ objects. This process could certainly be done by Python but it will require further development. Moreover the input file is maybe an intermediate step to keep. First it allows to check how the Python package is working. The user could ignore the input file syntax but in case of a simulation crash, he can test it independently on the MBDyn executable. Moreover the C++ parser checks also for model consistency by requiring a 'control data' section. As a resume, the input file is probably a good idea for the systematic process of a solver.

For making the simulation flexible, the Python interface does not require the control data any more, this work is done in the mbdyn.control_data module. An user mistake will thus be issued by the MBDyn parser. Moreover every reference frames is supposed to have a distinct label, an integer, in order to describe the connectivity in the input file. The same remark applied for every item inside a group of nodes or elements. The groups are defined in the mbdyn.bindings.groups module. As explained in the previous section, the user will now use references to describe the topology of its model. As a result, the mbdyn.groups module will handle an automatic labelling of every item. That's why the BasicObject of the mbdyn.common module has a label as attribute. It will be the top class of every MBDyn item. The same label will also be used to retrieved the MBDyn instance generated by WraptMBDyn when parsing the input file (this step is performed by the get_mbdyn_instance method for each item). Finally, every information required by MBDyn needs also to be provided by the interface. The difference is that this last one will keep the information entered as Python object while it will need to be transformed as string for the text input file. This job is done by the ManagerObject in mbdyn.common that assures to have a Python value and a corresponding MBDyn one for every attribute concerned by the input file. The last step is to add every item to a Simulation instance. What about the other interface problems?

Object description

Another design philosophy is that the user is supposed to know what he wants to simulate and as a consequence he has also an idea about the results. Clearly, the user knows which objects he wants (or he may be in a research phase) but he is anyway supposed to write a description in the input file. He will choose an item from the library, he has to, else there is no simulation. So this idea can be expressed by creating an instance and keeping a reference to it. For the user, doing:
   node1 = StructuralNode()
   node1.set_type("static")
   node2 = StructulalNode()

   # Description to continue
is not not more painful than writing:
   begin: nodes;
       structural: 1, static,
       # description of node 1 goes here
       
       structural: 1, dynamic,
       # description of node 2 goes here
   
   end: nodes;

For the developers the situation becomes however really easier, because this is the user that choose the class from the library and create the instance. Thus he expresses more ideas than in a plain text file and he will directly dialog with the instance. In that case no tests are needed, if he fails in that part he will get a Python error. For scientists dealing with multibody dynamics problem, the Python bases are accessible very quickly. Similar simulations as the usual input files could be reproduced without many efforts.

But more can be done from that approach. When the user creates an instance, default settings are applied on it. They are described in the set_default method of every object, as a start the reader can see mbdyn.nodes and mbdyn.elements_base. If the user sets the node or element relative to a reference frame, the default properties will be updated in that base. There are however still some problems, warned in that place. But the design idea is to apply method on the object for handling options. For example the attributes that need to be saved on the item will be specified by the will_save, will_save_only and will_save_nothing methods applied on the Record class (base of the BasicObject and thus every item). Then the internal saving process is addressed in details in the post-processing section.

Finally the Python syntax is a convenient way to build custom objects. In the section 1, the last user requirement is finally to re use the work done on the available library, hiding the complex implementation of each object, but then extend one of them to his problem. How is this idea achieved in computer science? By an inheritance. How is it done in Python? In one line (including the class definition) or two lines in case of a custom constructor needed. The last section illustrates this idea by an example. If the user can now describe its problem from Python, how can he retrieves the results?

2.3 Post processing

During the iterations, the results are added on Python objects and not saved in text files anymore. As a consequence the post-processing is now different because results are attributes of objects. But how is a simulation exactly saved? Is this approach really easier for exploring MBDyn outputs?

Python interaction

As described in the previous section, the user chooses the properties that he wants to save on an object. Thus he gets back his results according to the description of its problem, without the step to process text files. This step is achieved by the Record class in mbdyn.record. Then the results need to be saved at each time step, for this each concerned object has as a save method. The main save method, that will save all the object tree is performed by the Simulation. However the Simulation object is not aware about when a time step has been finished, because this part deals with the MBDyn service. That's why the save method of the Simulation is called by WraptMBDyn in the mbdyn.bindings module. At the end of the simulation, the complete object tree has been filled with results. The PyMBDynFile class is for that reason a container that will save simulations into a file. Then the user can load his simulation with results in another script. He has the same objects tree but that time filled with results. The usual MBDyn behavior, saving all the results in a file, can nevertheless be find back by the method write_mbdyn_files on WraptMBDyn. Thanks to the Python syntax, it is already easier to achieve post-processing in an interactive section than having to process text files. Is is possible to do more from that object-oriented approach?

Graphical exploration of results

Integrating MBDyn as an application programming interface in Python has open new doors for a graphical user interface. It was not the first objective but developing new functionalities bring often to such results. Programing in Python is easy comparing to the C++ language, with a minimum of efforts it directly brings the problem to the point of interest. As a result in the interface package MBDyn objects has become able to deal with graphical libraries. The object tree organisation makes the interface design very evident. Each group will be stored as a part of a tree. Each item will have actions to offer from a menu according to its available results. The toolkit used for the graphical user interface is GTK, the representation of 3D quantities is assured by VTK and the figures are plotted with Matplotlib. This is actually one more advantage of Python to be a popular language in free software projects. Most of the important libraries have Python bindings, moreover supplied in very reliable packages. For example many projects are using pyGTK. The development of the mbdyn.interface package was started to solve mechanical problems in wind turbine engineering. The running application is only available in the wind-sim-suite project but the work done could be re-used to visualize MBDyn results.

Project for final animation

Another project has been started for realizing a graphical user interface with Blender: Blended MBDyn by John Kollman. With the 3D environment, the user describes his problem geometry and from it defined MBDyn objects. An input file is written, run, and each defined node will get results as Blender IPO curves. This work has inspired a big part of this package. However the problems encountered were the same as described in the section 1. The user would like to deal with a specific object for its problem (like a blade or a tower for a wind turbine) but the MBDyn simulation used only three kinds of items: reference frames, nodes and elements. During a research phase, the final geometry is not the most important because the physical model is still being implemented. Instead an interactive visualization of MBDyn items is needed, mainly reference frames and nodes, but in that case a custom graphical user interface needs to be developed. That's why the last problem is actually a programing fact: Blender is an application, not a library. This is a suit of tools for creating 3D contents, thus it has not been designed for multibody problems. Developing graphical interface is not easy because specific OpenGL widgets are used. For example this is difficult to implement a tree or set a menu on a custom object, it would require development of the C sources as a Blender extension. The application has however a very important functionality: a 3D scene can be described from Python. The MBDyn items that make a simulation could deformed a geometry. Only the position of the nodes will be needed in that case and they will deform a mesh representing the user model. Two requirements arise: the geometry description is part of the user work (done with the Blender interface or from Python) and then the node results will somehow deform it. This solution will be the final step of a MBDyn model: when the simulation run correctly, illustrative results with meshes, textures and lights are rendered from Blender. The current package and the Blended MBDyn project are glue by Python but the connection has not yet been done.

2.4 A resuming example

At that step it may be difficult to follow what represents each section and what has been concretely achieved. The following example try to illustrate it. It could be bombarded with criticisms for being actually too simple and idealistic. It is true that the package has not yet reached a mature status and some solutions are probably more complicated than what they should be. But this section aims at giving a picture of the functionalities researched.

If the user wants to design a rotor, he will certainly set a hub node. The subject is dealing with mechanical engineering, so this node will be a structural node. First, let's create it:
   class HubNode(StructuralNode):
       pass
The inheritance is done but the new class has nothing new to offer. The node will be dynamic because it will deal with acceleration quantities and be attached to a body element for setting an inertia:
   class HubNode(StructuralNode):

       def __init__(self, name="hub node"):
           StructuralNode.__init__(self, name)
           self.set_type("dynamic")
Notice the inheritance from the StructuralNode achieved in two lines. The present application programming interface will give you the definition of every object. As the node is at the hub, it is supposed that only the rotational speed needs to be saved during the simulation. The too big simplicity of this example is here. In case the user wants to save the default quantities (position, rotation matrix..) and its own, he has also to customize the init_results method of the Record class for executing all the needed actions before saving the results. But the present example stays only with one quantity:
   class HubNode(StructuralNode):

       def __init__(self, name="hub node"):
           StructuralNode.__init__(self, name)
           self.set_type("dynamic")
           self.rotational_speed = 0.
           self.will_save_only("rotational_speed")
Now the save method of the Simulation will be called at each time step by WraptMBDyn. The one of the HubNode will be overriden:
   class HubNode(StructuralNode):

       def __init__(self, name="hub node"):
           StructuralNode.__init__(self, name)
           self.set_type("dynamic")
           self.rotational_speed = 0.
           self.will_save_only("rotational_speed")

       def save(self):
           rot_array = self.mbdyn_inst.get_rotational_speed()
           self.rotational_speed = rot_array[2][0]
           self.save_results()
The mbdyn_inst is a reference to the MBDyn instance computed by the C++ code. This work is provided by the Simulation. As a result, its rotational speed vector will be got at each time step but the user knows from its problem description that only the value along the z axis is relevant. For the HubNode, this is the only result saved. The hub node will now be used for a simulation description:
   hub_node = HubNode("hub node")
   # initial conditions go here (reference frame, 
   # position, rotation_matrix...)

   # Problem description with others nodes and elements
   
   simu = Simulation()
   simu.add_node(hub_node)
   # others items are added

   simu.run()

   pyf = pyMBDynFile("rotor_simu.pymb", "w")
   pyf.add(simu)
   pyf.close()
All this code will be written in a Python script, for example 'simulate_rotor.py' and the run will be achieved by python simulate_rotot.py. It will create a file 'rotor_simu.pymb' with the run simulation contained inside. An important remark: the simulation does not know a HubNode but the user has inherited from the StructuralNode class so it works. Moreover, his code described in the save method is executed at each time step. At the end, everything is saved in the pyMBDynFile container, it can now be used in a loading script, 'load.py' :
   pyf = pyMBDynFile("rotor_simu.pymb")
   simu = pyf.simulations[0]
   strnodes = simu.nodes.structurals
Now IPython can be used for interaction. The command ipython load.py will open a session after executing the script:
   >>> hub = strnodes.get("hub node")
   >>> hub.results
   [rotational_speed]

   >>> import pylab as P
   >>> P.plot(simu.results.time, hub.results.rotational_speed)
   >>> P.show()

The rotational speed is available through the results attribute, instance of Results defined in mbdyn.quantity. With the three last lines, a figure showing the rotational speed according to the time should be plotted.

An drawnback can also be noticed, the user has created a specific object but at the end it is saved like any other node by the MBDyn simulation. That's why the node reference needs to be got back from its name. The solution would be that the user writes also its own Simulation class, then he could handle a special object. Moreover any object could be saved with the simulation, assuming that it inherits from the Record one. The next step would be to provide a neat interface to such requirements. This approach was however already used in the wind-sim-suite project, where a wind turbine object can be added to the simulation.

3 Conclusion

To conclude Python does not execute any integration but is just a language replacing the input file to describe a simulation. Its batteries included are however not a joke. Imitating the current way to write input files is possible but a complete programming language is now placed in the user hands. As a result he can describe its own objects that manipulate MBDyn ones, keep references of computed items, define its own and with some efforts extend the library for post-processing. This great flexibility has a price: the requirement to learn Python. But the reward is worth the hassle because the script is interactive and especially suited to his problem. Last but not least, third part packages could potentially improve the scientific usability of MBDyn: SciPy, PyDSTool, IPython, Matplotlib... to name a few. Is there still a reason to not dive into Python?

On the developer side, the significant advances are to turn MBDyn into a Python service and give more responsibility to the user. He is now supposed to know about an object when describing a simulation. In clear the parts where MBDyn was assisting a text file writer is now replaced by a Python interpreter assisting a script programmer. Is it a developer laziness? No, because people simulating multibody problems will surely know about numerical analysis. On that point Python is an user friendly language that has always powerful concepts hidden for later use. Concerning the MBDyn service, most of the actions done at run time should of course be performed in C++ for speed reasons. New Python functionalities should be translated step by step. Nevertheless when a limitation is encountered, a Python framework seems more productive for development than dealing with C++ sources.


Version: 0.2-r1

Submodules [hide private]

Variables [hide private]
  __author_utf8__ = 'Andr\xc3\xa9 ESPAZE'
  __author_email__ = 'ded.espaze@laposte.net'
  __mbdyn_developers__ = ['Paolo Mantegazza', 'Massimiliano Lanz...
Variables Details [hide private]

__mbdyn_developers__

Value:
['Paolo Mantegazza',
 'Massimiliano Lanz',
 'Gian Luca Ghiringhelli',
 'Pierangelo Masarati',
 'Giuseppe Quaranta',
 'Marco Morandini',
 'Stefania Gualdi',
 'Michele Attolico',
...