Home | Trees | Indices | Help |
---|
|
In short, the motivation is to turn MBDyn as a service that can be controlled from Python. The mbdyn module is mainly an extension to the MBDyn functionalities by avoiding the user to write an input file and instead deal with objects. The bindings part will introduce communication. But why was it needed?
In MBDyn the C++ objects are updated at each iteration, no matter if
the solver has reached a step of equilibrium or not. The main method
called on an object is AssRes
, which stands obviously for
'assemble residual' during the research of equilibrium between
internal and external work (the Newton Raphson iterations). If an user
wants to develop a new object, he has to provide the necessary methods
called by the solver. Then he can link it to MBDyn thanks to the
ltdl library from the libtool
package. Above the problem to develop code in a low language, the main
issue is at runtime: the object will update its residual in C++. If for
finding the residual an external library needs to be called, this
operation will also have to be implemented in C++. This is particularly
tedious when the external calculations will need many information about
the simulation status (node positions, orientation matrix, time and so
on). All the topological work of keeping objects reference will have to
be done in C++ and all the information that those objects return are
inconvenient comparing to a dynamic language (the Vec3
object in section 2 gives an example). Moreover for the development
part, speed is not critical because the simulation is run for few
iterations. Instead, a convenient way to access any value is more
interesting for starting coupling calculations.
The tool used to establish a connection between C++ and Python is SWIG. The interface is
contained in the file swigModule.py
, that is automatically
generated (and for this reason not present in the Epydoc documentation)
from SWIG input files. The process also creates the library
_swigModule.so
, dynamically linked to the MBDyn shared
library, libmbdyn.so
. The result is an access to MBDyn C++
objects from a script language.
However the interface achieved is raw, it means that the user has to
deal with the C++ methods used in MBDyn and gets back pointer to C++
instances. For example a call like node.GetXCurr()
will
return a Vec3
vector object defined in
'libmbmath.i'. For filling or getting values from such a vector,
the set_value
and get_value
methods need to
be used. It is clearly easier to return Numpy arrays for the
user. Moreover as the SWIG interface returns references, if the value
needs to be saved at a particular time step the vector object will have
to be copied. Numpy provided a convenient way to deal with such problem
by array.copy()
. That's why every vector or matrix will
correspond to a Numpy array.
For controlling the solver, the class SolverBinding
has been written in the files 'solver_bindings.h' and
'solver_binings.cc'. The SWIG interface to those files is in
'solver.i'. The main modification is to have split the
Run
method of the MBDyn Solver
class
(defined in 'solver.h') into three methods that control the
resolution: run_init
, run_first_step
and
run_one_step
. Moreover the most important values, like
the time and time step, have been made accessible trough a float
(MBDyn was using a special declaration).
main
function of 'mbdyn.cc' has been transformed into the class
WraptMBDyn
, defined in 'mbdyn_bindings.h. The
interface to that class is in 'mbdyn.i' and has a
SolverBinding
instance under the attribute
solver
. By that way the solver control is achieved
through WraptMBDyn, a class that is going to receive a MBDyn
input file and thus represent the MBDyn service in Python. The next
step will be to establish a communication with MBDyn objects
describing the problem: the reference frames, nodes and elements.
In MBDyn, the nodes and elements are organised into groups by the
DataManager
, describe in 'dataman.h'. The
reference frames are however part of the MBDynParser
,
defined in 'mbpar.h'. The DataManager
is
attribute of the SolverBinding
while the
MBDynParser
is attribute of WraptMBDyn
. The
corresponding SWIG interfaces can then be found in 'dataman.i'
and 'mbdyn_parser.i'. Any of those items, reference frame,
node or element, will have to be accessed.
Inside a group all the items are linked by pointers. The group
keeps the reference of the first item, then every item has a
reference to the next one (the definition of a linked
list). This design allows to loop in all the items and thus calls
the needed methods for a resolution step. Again the
AssRes
method is an example. In the Python world, each
linked list will become a simple list made of references to MBDyn
items. This is the work of the mbdyn.bindings.groups. Moreover for nodes and
elements, a manager of all the groups will be defined by Groups.
The situation is simpler for the reference frames as there is only
one group and one class, so everything is contained in mbdyn.bindings.frames.
solver
. When scanning the
input file, the init method will create the groups that are found
and store the references. Thus any object will be accessible by a
Python syntax, the last step is the communication.
This section only concerns nodes and elements, that are however
the most important items. In the C++ code the groups of the
DataManager
use a single class definition for different
classes. This MBDyn polymorphism is described by Node
in
'elem.h' and Elem
in 'elem.h'. Thus the
DataManager
returns pointers to those classes but the
object of interest is more specific. It was not a problem for MBDyn
because the developers knew what were the methods defined as virtual. In case of an user interface, any method
may need to be called.
The starting point was to provide a general node and element class
in mbdyn.bindings.basic_objects. When parsing the MBDyn
input file, those items were organised into groups, as explained in
the section 3.2. Each group have also a general class acting as a
template. For example the class Force
in
'force.h' is the top class of the force group. The same remark
applied for Joint
in 'joint.h. Now for getting
down of the hierarchy, a dynamic cast process will have to be used
from C++. Each group will need to have a function, executing a
dynamic cast, that will convert a reference from a top class to a
group reference. This step is for example achieved by
convert_to_force
in 'force.cc' and
'force.i' contains its SWIG interface. Inside a group, more
specific classes are also available. So the conversion process may be
pushed further by applying one more dynamic cast and finally getting
the reference of interest. The initial reference can finally be lost
because the lowest class of a hierarchy inherits all the methods from
the top classes.
mbdyn_inst
attribute for each corresponding object of
the mbdyn package. And
the user can even keep such reference in its own script, thus using
the MBDyn results that he wants at run time. Nevertheless to get back
useful objects, an implementation needs to be done first in C++, then
in SWIG and finally in Python. As a consequence, the next section
explains how to keep the simplest interface.
This module is only supposed to supervised the MBDyn process, the addition of new functionalities should not be done in that part. The goal is only to access some of the objects and see if their results match the MBDyn ones. However, this module will not handle the saving of values nor add new methods on MBDyn objects. What is the development achieved so far?
Based on that philosophy, the structural nodes have been wrapped in
mbdyn.bindings.nodes. Some of the forces and joints
have been implemented in mbdyn.bindings.forces and mbdyn.bindings.joints. The possibility to access
values at run time has however changed the MBDyn way of work about
drivers. The user is supposed to describe its object behavior in the
input file. He can for example modified the amplitude of a force
according to time but the direction is kept constant according to the
initial value or according to the node orientation matrix. Those two
classes are ConservativeForce
and
FollowerForce
defined in 'strforce.h'. As expected
in the AssRes
method of those two classes, once the
amplitude of the driver got, its multiplication by the direction
returns the force value. Now in the file 'strforce.h', the
BindingsForce
inherits from the
ConservativeForce
, so all the needed methods used in MBDyn
are present. Then one line is modify in the AssRes
method:
the force value is a vector provided by Python. This interface,
described in 'force.i', supply also the
FollowerForce
functionalities because at each time step
the user can get the rotation matrix of the corresponding node. He can
also applied the force value that he wants, to a degree that can make
the solver crash, but that will be surely part of its research phase.
The achievement is to fill a force value at run time by using the nodes
properties from Python.
update
and
save
methods that will be references to the Simulation
ones. To conclude, extra services need to be developed in mbdyn because Python
references from that package can be seen as MBDyn instances.
|
|||
|
Home | Trees | Indices | Help |
---|
Generated by Epydoc 3.0beta1 on Thu Aug 30 11:45:41 2007 | http://epydoc.sourceforge.net |