Пример #1
0
    def __init__(self, io_filename = None, mode = 'w',
                 broadphase=None, osi=None, shape_filename=None,
                 set_external_forces=None, length_scale=None):

        if io_filename is None:
            self._io_filename = '{0}.hdf5'.format(
                os.path.splitext(os.path.basename(sys.argv[0]))[0])
        else:
            self._io_filename = io_filename
        self._mode = mode
        self._broadphase = broadphase
        self._osi = osi
        self._static_origins = []
        self._static_orientations = []
        self._static_transforms = []
        self._static_cobjs = []
        self._shape = None
        self._shapeid = dict()
        self._static_data = None
        self._velocities_data = None
        self._dynamic_data = None
        self._cf_data = None
        self._solv_data = None
        self._input = None
        self._nslaws = None
        self._out = None
        self._data = None
        self._joints = None
        self._io = MechanicsIO()
        self._set_external_forces = set_external_forces
        self._shape_filename = shape_filename
        self._number_of_shapes = 0
        self._number_of_dynamic_objects = 0
        self._number_of_static_objects = 0
        self._length_scale = length_scale
        self._keep = []
Пример #2
0
class Hdf5():
    """a Hdf5 context manager reads at instantiation the translations and
       orientations of collision objects from hdf5 file

       It provides functions to output translations and orientations in
       the same file during simulation (output is done by default in
       pos.dat)

       with:
         time : float
         object_id : the object id (int)
         px, py, pz : components of the translation (float)
         ow, ox, oy oz : components of an unit quaternion (float)

    """

    def __init__(self, io_filename = None, mode = 'w',
                 broadphase=None, osi=None, shape_filename=None,
                 set_external_forces=None, length_scale=None):

        if io_filename is None:
            self._io_filename = '{0}.hdf5'.format(
                os.path.splitext(os.path.basename(sys.argv[0]))[0])
        else:
            self._io_filename = io_filename
        self._mode = mode
        self._broadphase = broadphase
        self._osi = osi
        self._static_origins = []
        self._static_orientations = []
        self._static_transforms = []
        self._static_cobjs = []
        self._shape = None
        self._shapeid = dict()
        self._static_data = None
        self._velocities_data = None
        self._dynamic_data = None
        self._cf_data = None
        self._solv_data = None
        self._input = None
        self._nslaws = None
        self._out = None
        self._data = None
        self._joints = None
        self._io = MechanicsIO()
        self._set_external_forces = set_external_forces
        self._shape_filename = shape_filename
        self._number_of_shapes = 0
        self._number_of_dynamic_objects = 0
        self._number_of_static_objects = 0
        self._length_scale = length_scale
        self._keep = []

    def __enter__(self):
        if self._set_external_forces is None:
            self._set_external_forces = self.apply_gravity

        if self._length_scale is None:
            self._length_scale = 1  # 1 => m, 1/100. => cm

        self._out = h5py.File(self._io_filename, self._mode)
        self._data = group(self._out, 'data')
        self._ref = group(self._data, 'ref')
        self._joints = group(self._data, 'joints')
        self._static_data = data(self._data, 'static', 9)
        self._velocities_data = data(self._data, 'velocities', 8)
        self._dynamic_data = data(self._data, 'dynamic', 9)
        self._cf_data = data(self._data, 'cf', 15)
        self._solv_data = data(self._data, 'solv', 4)
        self._input = group(self._data, 'input')
        self._nslaws = group(self._data, 'nslaws')

        if self._shape_filename is None:
            self._shape = ShapeCollection(self)
        else:
            self._shape = ShapeCollection(self._shape_filename)

        return self

    def __exit__(self, type_, value, traceback):
        self._out.close()


    def apply_gravity(self, body):
        g = constants.g / self._length_scale
        weight = [0, 0, - body.massValue() * g]
        body.setFExtPtr(weight)

# hdf5 structure

    def shapes(self):
        """
        Shapes : parameterized primitives or user defined
                 (convex set or meshes)
        """
        return self._ref

    def static_data(self):
        """
        Coordinates and orientations of static objects.
        """
        return self._static_data

    def dynamic_data(self):
        """
        Coordinates and orientations of dynamics objects.
        """
        return self._dynamic_data

    def contact_forces_data(self):
        """
        Contact points informations.
        """
        return self._cf_data

    def solver_data(self):
        """
        Solver output
        """
        return self._solv_data

    def instances(self):
        """
        Scene objects.
        """
        return self._input

    def nonsmooth_laws(self):
        """
        Non smooth laws between group of contactors.
        """
        return self._nslaws

    def joints(self):
        """
        Joints between dynamic objects or between an object and the scenery.
        """
        return self._joints

    def importNonSmoothLaw(self, name):
        if self._broadphase is not None:
            nslawClass = getattr(Kernel, self._nslaws[name].attrs['type'])
            # only this one at the moment
            assert(nslawClass == Kernel.NewtonImpactFrictionNSL)
            nslaw = nslawClass(float(self._nslaws[name].attrs['e']), 0.,
                               float(self._nslaws[name].attrs['mu']), 3)
            self._broadphase.insert(nslaw,
                                    int(self._nslaws[name].attrs['gid1']),
                                    int(self._nslaws[name].attrs['gid2']))

    def importBRepObject(self, name, translation, orientation,
                         velocity, contactors, mass, given_inertia, body_class,
                         shape_class, face_class, edge_class):

        if body_class is None:
            body_class = OccBody

        if given_inertia is not None:
            inertia = given_inertia
        else:
            inertia = np.eye(3)

        if mass == 0.:
            # a static object
            pass

        else:
            body = body_class(translation + orientation, velocity, mass, inertia)
            for contactor in contactors:

                # /!\ shared pointer <-> python ref ...
                shape_instantiated = self._shape.get(contactor.name,
                                                     shape_class, face_class, edge_class)
                self._keep.append(shape_instantiated)

                body.addContactShape(shape_instantiated,
                                     contactor.translation,
                                     contactor.orientation,
                                     contactor.group)
            self._set_external_forces(body)

            # add the dynamical system to the non smooth
            # dynamical system
            nsds = self._broadphase.model().nonSmoothDynamicalSystem()
            nsds.insertDynamicalSystem(body)
            nsds.setOSI(body, self._osi)
            nsds.setName(body, str(name))


    def importObject(self, name, translation, orientation,
                     velocity, contactors, mass, inertia, body_class, shape_class):

        if body_class is None:
            body_class = BulletDS

        if self._broadphase is not None and 'input' in self._data:
            if mass == 0.:
                # a static object
                rbase = btQuaternion(orientation[1],
                                     orientation[2],
                                     orientation[3],
                                     orientation[0])

                for c in contactors:
                    (w, x, y, z) = c.orientation
                    c_orientation = btQuaternion(x, y, z, w)
                    rc_orientation = mul(rbase, c_orientation)

                    c_origin = btVector3(c.translation[0],
                                         c.translation[1],
                                         c.translation[2])

                    rc_origin = quatRotate(rbase, c_origin)

                    rc_sorigin = btVector3(rc_origin.x() + translation[0],
                                           rc_origin.y() + translation[1],
                                           rc_origin.z() + translation[2])

                    static_cobj = btCollisionObject()
                    static_cobj.setCollisionFlags(
                        btCollisionObject.CF_STATIC_OBJECT)

                    self._static_origins.append(rc_sorigin)

                    self._static_orientations.append(rc_orientation)
                    transform = btTransform(rc_orientation)
                    transform.setOrigin(rc_sorigin)
                    self._static_transforms.append(transform)
                    static_cobj.setWorldTransform(transform)

                    static_cobj.setCollisionShape(
                        self._shape.get(c.name))
                    self._static_cobjs.append(static_cobj)
                    self._broadphase.addStaticObject(static_cobj,
                                                     int(c.group))

            else:
                # a moving object

                bws = BulletWeightedShape(
                    self._shape.get(contactors[0].name), mass)

                if inertia is not None:
                    bws.setInertia(inertia[0], inertia[1], inertia[2])

                body = body_class(bws,
                                  translation + orientation,
                                  velocity,
                                  contactors[0].translation,
                                  contactors[0].orientation,
                                  contactors[0].group)

                for contactor in contactors[1:]:
                    shape_id = self._shapeid[contactor.name]

                    body.addCollisionShape(self._shape.get(contactor.name),
                                           contactor.translation,
                                           contactor.orientation,
                                           contactor.group)

                # set external forces
                self._set_external_forces(body)

                # add the dynamical system to the non smooth
                # dynamical system
                nsds = self._broadphase.model().nonSmoothDynamicalSystem()
                nsds.insertDynamicalSystem(body)
                nsds.setOSI(body, self._osi)
                nsds.setName(body, str(name))

    def importJoint(self, name):
        if self._broadphase is not None:
            topo = self._broadphase.model().nonSmoothDynamicalSystem().\
                   topology()

            joint_class = getattr(Joints,
                                 self.joints()[name].attrs['type'])

            joint_nslaw = EqualityConditionNSL(5)

            ds1_name = self.joints()[name].attrs['object1']
            ds1 = topo.getDynamicalSystem(ds1_name)

            if 'object2' in self.joints()[name].attrs:
                ds2_name = self.joints()[name].attrs['object2']
                ds2 = topo.getDynamicalSystem(ds2_name)
                joint = joint_class(ds1,
                                    ds2,
                                    self.joints()[name].attrs['pivot_point'],
                                    self.joints()[name].attrs['axis'])
                joint_inter = Interaction(5, joint_nslaw, joint)
                self._broadphase.model().nonSmoothDynamicalSystem().\
                    link(joint_inter, ds1, ds2)

            else:
                joint = joint_class(ds1,
                                    self.joints()[name].attrs['pivot_point'],
                                    self.joints()[name].attrs['axis'])

                joint_inter = Interaction(5, joint_nslaw, joint)
                self._broadphase.model().nonSmoothDynamicalSystem().\
                    link(joint_inter, ds1)

    def importScene(self, body_class, shape_class, face_class, edge_class):
        """
        Import into the broadphase object all the static and dynamic objects
        from hdf5 file
        """

        for shape_name in self._ref:
            self._shapeid[shape_name] = self._ref[shape_name].attrs['id']
            self._number_of_shapes += 1

        def floatv(v):
            return [float(x) for x in v]

        # import dynamical systems
        if self._broadphase is not None and 'input' in self._data:

            for (name, obj) in self._input.items():
                input_ctrs = [ctr for _n_, ctr in obj.items()]
                mass = obj.attrs['mass']
                translation = obj.attrs['translation']
                orientation = obj.attrs['orientation']
                velocity = obj.attrs['velocity']
                contactors = [Contactor(ctr.attrs['name'],
                                        int(ctr.attrs['group']),
                                        floatv(ctr.attrs['translation']),
                                        floatv(ctr.attrs['orientation']))
                              for ctr in input_ctrs]

                if 'inertia' in obj.attrs:
                    inertia = obj.attrs['inertia']
                else:
                    inertia = None


                if True in ('type' in self.shapes()[ctr.attrs['name']].attrs
                            and self.shapes()[ctr.attrs['name']].attrs['type'] in ['brep', 'step']
                            for ctr in input_ctrs):
                    # Occ object
                    self.importBRepObject(name, floatv(translation), floatv(orientation),
                                          floatv(velocity), contactors, float(mass),
                                          inertia, body_class, shape_class, face_class, edge_class)
                else:
                    # Bullet object
                    self.importObject(name, floatv(translation), floatv(orientation),
                                      floatv(velocity), contactors, float(mass),
                                      inertia, body_class, shape_class)

            # import nslaws
            for name in self._nslaws:
                self.importNonSmoothLaw(name)

            for name in self.joints():
                self.importJoint(name)

    def outputStaticObjects(self):
        """
        Outputs translations and orientations of static objects
        """
        time = self._broadphase.model().simulation().nextTime()
        idd = -1
        p = 0
        self._static_data.resize(len(self._static_transforms), 0)

        for transform in self._static_transforms:
            translation = transform.getOrigin()
            rotation = transform.getRotation()
            self._static_data[p, :] = \
                [time,
                 idd,
                 translation.x(),
                 translation.y(),
                 translation.z(),
                 rotation.w(),
                 rotation.x(),
                 rotation.y(),
                 rotation.z()]
            idd -= 1
            p += 1

    def outputDynamicObjects(self):
        """
        Outputs translations and orientations of dynamic objects
        """

        current_line = self._dynamic_data.shape[0]

        time = self._broadphase.model().simulation().nextTime()

        positions = self._io.positions(self._broadphase.model())

        self._dynamic_data.resize(current_line + positions.shape[0], 0)

        times = np.empty((positions.shape[0], 1))
        times.fill(time)

        tidd = np.arange(1,
                         positions.shape[0] + 1).reshape(
                         positions.shape[0], 1)

        self._dynamic_data[current_line:, :] = np.concatenate((times, tidd,
                                                               positions),
                                                               axis=1)


    def outputVelocities(self):
        """
        Output velocities of dynamic objects
        """

        current_line = self._dynamic_data.shape[0]

        time = self._broadphase.model().simulation().nextTime()

        velocities = self._io.velocities(self._broadphase.model())

        self._velocities_data.resize(current_line + velocities.shape[0], 0)

        times = np.empty((velocities.shape[0], 1))
        times.fill(time)

        tidd = np.arange(1,
                         velocities.shape[0] + 1).reshape(
                         velocities.shape[0],
                         1)

        self._velocities_data[current_line:, :] = np.concatenate((times, tidd,
                                                                  velocities),
                                                                  axis=1)



    def outputContactForces(self):
        """
        Outputs contact forces
        """
        if self._broadphase.model().nonSmoothDynamicalSystem().\
                topology().indexSetsSize() > 1:
            time = self._broadphase.model().simulation().nextTime()
            contact_points = self._io.contactPoints(self._broadphase.model())

            if contact_points is not None:

                current_line = self._cf_data.shape[0]
                self._cf_data.resize(current_line + contact_points.shape[0], 0)
                times = np.empty((contact_points.shape[0], 1))
                times.fill(time)

                self._cf_data[current_line:, :] = \
                    np.concatenate((times,
                                    contact_points),
                                   axis=1)

    def outputSolverInfos(self):
        """
        Outputs solver #iterations & precision reached
        """

        time = self._broadphase.model().simulation().nextTime()
        so = self._broadphase.model().simulation().oneStepNSProblem(0).\
            numericsSolverOptions()

        current_line = self._solv_data.shape[0]
        self._solv_data.resize(current_line + 1, 0)
        if so.solverId == Numerics.SICONOS_GENERIC_MECHANICAL_NSGS:
            iterations = so.iparam[3]
            precision = so.dparam[2]
            local_precision = so.dparam[3]
        elif so.solverId == Numerics.SICONOS_FRICTION_3D_NSGS:
            iterations = so.iparam[7]
            precision = so.dparam[1]
            local_precision = 0.
        # maybe wrong for others
        else:
            iterations = so.iparam[1]
            precision = so.dparam[1]
            local_precision = so.dparam[2]

        self._solv_data[current_line, :] = [time, iterations, precision,
                                            local_precision]

    def addMeshFromString(self, name, shape_data):
        """
        Add a mesh shape from a string.
        Accepted format : mesh encoded in VTK .vtp format
        """
        if name not in self._ref:

            shape = self._ref.create_dataset(name, (1,),
                                             dtype=h5py.new_vlen(str))
            shape[:] = shape_data
            shape.attrs['id'] = self._number_of_shapes
            shape.attrs['type'] = 'vtp'
            self._shapeid[name] = shape.attrs['id']
            self._number_of_shapes += 1

    def addMeshFromFile(self, name, filename):
        """
        Add a mesh shape from a file.
        Accepted format : .stl or mesh encoded in VTK .vtp format
        """
        if name not in self._ref:

            if os.path.splitext(filename)[-1][1:] == 'stl':
                reader = vtk.vtkSTLReader()
                reader.SetFileName(filename)
                reader.Update()

                with tmpfile() as tmpf:
                    writer=vtk.vtkXMLPolyDataWriter()
                    writer.SetInputData(reader.GetOutput())
                    writer.SetFileName(tmpf[1])
                    writer.Write()

                    shape_data = str_of_file(tmpf[1])

            else:
                assert os.path.splitext(filename)[-1][1:] == 'vtp'
                shape_data = str_of_file(filename)


            self.addMeshShapeFromString(name, shape_data)

    def addBRepFromString(self, name, shape_data):
        """
        Add a brep contained in a string.
        """
        if name not in self._ref:
            shape = self._ref.create_dataset(name, (1,),
                                             dtype=h5py.new_vlen(str))
            if type(shape_data) == str:
                # raw str
                shape[:] = shape_data
            else:
                # __getstate__ as with pythonocc
                shape[:] = shape_data[0]
                shape.attrs['occ_indx'] = shape_data[1]
                
            shape.attrs['id'] = self._number_of_shapes
            shape.attrs['type'] = 'brep'

            self._shapeid[name] = shape.attrs['id']
            self._number_of_shapes += 1

    def addOccShape(self, name, occ_shape):
        """
        Add an OpenCascade TopoDS_Shape
        """

        if name not in self._ref:

            from OCC.STEPControl import STEPControl_Writer, STEPControl_AsIs

            # step format is used for the storage.
            step_writer = STEPControl_Writer()
            step_writer.Transfer(occ_shape, STEPControl_AsIs)

            shape_data = None

            with tmpfile() as tmpf:

                status = step_writer.Write(tmpf[1])

                tmpf[0].flush()
                shape_data = str_of_file(tmpf[1])

                shape = self._ref.create_dataset(name, (1,),
                                                 dtype=h5py.new_vlen(str))
                shape[:] = shape_data
                shape.attrs['id'] = self._number_of_shapes
                shape.attrs['type'] = 'step'
                self._shapeid[name] = shape.attrs['id']
                self._number_of_shapes += 1

    def addShapeDataFromFile(self, name, filename):
        """
        Add shape data from a file.
        """
        if name not in self._ref:
            shape = self._ref.create_dataset(name, (1,),
                                             dtype=h5py.new_vlen(str))
            shape[:] = str_of_file(filename)
            shape.attrs['id'] = self._number_of_shapes
            try:
                shape.attrs['type'] = os.path.splitext(filename)[1][1:]
            except:
                shape.attrs['type'] = 'unknown'

            self._shapeid[name] = shape.attrs['id']
            self._number_of_shapes += 1


    def addContactFromBRep(self, name, brepname, contact_type,
                           index, collision_group=0, associated_shape=None):
        """
        Add contact reference from a previously added brep.
        """
        if name not in self._ref:
            shape = self._ref.create_dataset(name, (1,),
                                             dtype=h5py.new_vlen(str))
            shape.attrs['id'] = self._number_of_shapes
            shape.attrs['type'] = 'brep'
            shape.attrs['contact'] = contact_type
            shape.attrs['brep'] = brepname
            shape.attrs['index'] = index
            if associated_shape is not None:
                shape.attrs['associated_shape'] = associated_shape
            self._shapeid[name] = shape.attrs['id']
            self._number_of_shapes += 1

    def addContactFromOccShape(self, name, occ_shape_name, contact_type,
                               index, collision_group=0, associated_shape=None):
        """
        Add contact reference from a previously added brep.
        """
        if name not in self._ref:
            shape = self._ref.create_dataset(name, (1,),
                                             dtype=h5py.new_vlen(str))
            shape.attrs['id'] = self._number_of_shapes
            shape.attrs['type'] = 'step'
            shape.attrs['contact'] = contact_type
            shape.attrs['step'] = occ_shape_name
            shape.attrs['index'] = index
            if associated_shape is not None:
                shape.attrs['associated_shape'] = associated_shape
            self._shapeid[name] = shape.attrs['id']
            self._number_of_shapes += 1

    def addConvexShape(self, name, points):
        """
        Add a convex shape defined by a list of points.
        """
        if name not in self._ref:
            apoints = np.array(points)
            shape = self._ref.create_dataset(name,
                                             (apoints.shape[0],
                                              apoints.shape[1]))
            shape[:] = points[:]
            shape.attrs['type'] = 'convex'
            shape.attrs['id'] = self._number_of_shapes
            self._shapeid[name] = shape.attrs['id']
            self._number_of_shapes += 1

    def addPrimitiveShape(self, name, primitive, params):
        """
        Add a primitive shape.
        """
        if name not in self._ref:
            shape = self._ref.create_dataset(name, (1, len(params)))
            shape.attrs['id'] = self._number_of_shapes
            shape.attrs['type'] = 'primitive'
            shape.attrs['primitive'] = primitive
            shape[:] = params
            self._shapeid[name] = shape.attrs['id']
            self._number_of_shapes += 1

    def addObject(self, name, shapes,
                  translation,
                  orientation=[1, 0, 0, 0],
                  velocity=[0, 0, 0, 0, 0, 0],
                  mass=0, inertia=None):
        """
        Add an object with associated shapes.
        Contact detection is defined by a list of contactors.
        The initial translation is mandatory : [x, y z].
        If the mass is zero this is a static object.
        """

        if len(orientation) == 2:
            # axis + angle
            axis = orientation[0]
            assert len(axis) == 3
            angle = orientation[1]
            assert type(angle) is float
            n = sin(angle / 2.) / np.linalg.norm(axis)

            ori = [cos(angle / 2.), axis[0] * n, axis[1] * n, axis[2] * n]
        else:
            # a given quaternion
            ori = orientation

        if name not in self._input:

            obj = group(self._input, name)
            obj.attrs['mass'] = mass
            obj.attrs['translation'] = translation
            obj.attrs['orientation'] = ori
            obj.attrs['velocity'] = velocity

            if inertia is not None:
                obj.attrs['inertia'] = inertia

            for num, shape in enumerate(shapes):
                dat = data(obj, '{0}-{1}'.format(shape.name, num), 0)
                dat.attrs['name'] = shape.name
                if hasattr(shape, 'group'):
                    dat.attrs['group'] = shape.group
                if hasattr(shape, 'parameters') and \
                    shape.parameters is not None:
                    dat.attrs['parameters'] = shape.parameters
                dat.attrs['translation'] = shape.translation
                dat.attrs['orientation'] = shape.orientation

            if mass == 0:
                obj.attrs['id'] = - (self._number_of_static_objects + 1)
                self._number_of_static_objects += 1

            else:
                obj.attrs['id'] = (self._number_of_dynamic_objects + 1)
                self._number_of_dynamic_objects += 1

    def addNewtonImpactFrictionNSL(self, name, mu, e=0, collision_group1=0,
                                      collision_group2=0):
        """
        Add a nonsmooth law for contact between 2 groups.
        Only NewtonImpactFrictionNSL are supported.
        name is a user identifiant and must be unique,
        mu is the coefficient of friction,
        e is the coefficient of restitution on the contact normal,
        gid1 and gid2 define the group identifiants.

        """
        if name not in self._nslaws:
            nslaw = self._nslaws.create_dataset(name, (0,))
            nslaw.attrs['type'] = 'NewtonImpactFrictionNSL'
            nslaw.attrs['mu'] = mu
            nslaw.attrs['e'] = e
            nslaw.attrs['gid1'] = collision_group1
            nslaw.attrs['gid2'] = collision_group2

    def addJoint(self, name, object1, object2=None, pivot_point=[0, 0, 0],
                 axis=[0, 1, 0],
                 joint_class='PivotJointR'):
        """
        add a pivot joint between two objects
        """
        if name not in self.joints():
            joint = self.joints().create_dataset(name, (0,))
            joint.attrs['object1'] = object1
            if object2 is not None:
                joint.attrs['object2'] = object2
            joint.attrs['type'] = joint_class
            joint.attrs['pivot_point'] = pivot_point
            joint.attrs['axis'] = axis

    def run(self,
            with_timer=False,
            time_stepping=None,
            space_filter=None,
            body_class=None,
            shape_class=None,
            face_class=None,
            edge_class=None,
            length_scale=1,
            t0=0,
            T=10,
            h=0.0005,
            multipoints_iterations=True,
            theta=0.50001,
            Newton_max_iter=20,
            set_external_forces=None,
            solver=Numerics.SICONOS_FRICTION_3D_NSGS,
            itermax=100000,
            tolerance=1e-8):
        """
        Run a simulation from inputs in hdf5 file.
        parameters are:
          with_timer : use a timer for log output (default False)
          length_scale : gravity is multiplied by this factor.
                         1.     for meters (default).
                         1./100 for centimeters.
                         This parameter may be needed for small
                         objects because of Bullet collision margin (0.04).

          t0 : starting time (default 0)
          T  : end time      (default 10)
          h  : timestep      (default 0.0005)
          multiPointIterations : use bullet "multipoint iterations"
                                 (default True)
          theta : parameter for Moreau-Jean OSI (default 0.50001)
          Newton_max_iter : maximum number of iterations for
                          integrator Newton loop (default 20)
          set_external_forces : method for external forces
                                (default earth gravity)
          solver : default Numerics.SICONOS_FRICTION_3D_NSGS
          itermax : maximum number of iteration for solver
          tolerance : friction contact solver tolerance

        """

        from siconos.kernel import \
            Model, MoreauJeanOSI, TimeDiscretisation,\
            GenericMechanical, FrictionContact, NewtonImpactFrictionNSL

        from Siconos.numerics import SICONOS_FRICTION_3D_AlartCurnierNewton

        from siconos.mechanics.contact_detection.bullet import \
            btConvexHullShape, btCollisionObject, \
            btBoxShape, btQuaternion, btTransform, btConeShape, \
            BulletSpaceFilter, cast_BulletR, \
            BulletWeightedShape, BulletDS, BulletTimeStepping

        if set_external_forces is not None:
            self._set_external_forces = set_external_forces

        if time_stepping is None:
            time_stepping = BulletTimeStepping

        if space_filter is None:
            space_filter = BulletSpaceFilter

        # Model
        #
        model = Model(t0, T)

        # (1) OneStepIntegrators
        joints = list(self.joints())

        self._osi = MoreauJeanOSI(theta)

        # (2) Time discretisation --
        timedisc = TimeDiscretisation(t0, h)

        if len(joints) > 0:
            osnspb = GenericMechanical(SICONOS_FRICTION_3D_AlartCurnierNewton)
        else:
            osnspb = FrictionContact(3, solver)

        osnspb.numericsSolverOptions().iparam[0] = itermax
        osnspb.numericsSolverOptions().dparam[0] = tolerance
        osnspb.setMaxSize(16384)
        osnspb.setMStorageType(1)
        #osnspb.setNumericsVerboseMode(False)

        # keep previous solution
        osnspb.setKeepLambdaAndYState(True)

        # (5) broadphase contact detection
        self._broadphase = space_filter(model)
        if not multipoints_iterations:
            print("""
            ConvexConvexMultipointIterations and PlaneConvexMultipointIterations are unset
            """)
        else:
            if hasattr(self._broadphase, 'collisionConfiguration'):
                self._broadphase.collisionConfiguration().\
                    setConvexConvexMultipointIterations()
                self._broadphase.collisionConfiguration().\
                    setPlaneConvexMultipointIterations()

        # (6) Simulation setup with (1) (2) (3) (4) (5)
        simulation = time_stepping(timedisc)
        simulation.insertIntegrator(self._osi)
        simulation.insertNonSmoothProblem(osnspb)
        simulation.setNewtonMaxIteration(Newton_max_iter)

        k = 1

        self.importScene(body_class, shape_class, face_class, edge_class)

        model.initialize(simulation)

        self.outputStaticObjects()
        self.outputDynamicObjects()

        while simulation.hasNextEvent():

            print ('step', k)

            log(self._broadphase.buildInteractions, with_timer)\
                (model.currentTime())

            log(simulation.computeOneStep, with_timer)()

            log(self.outputDynamicObjects, with_timer)()

            log(self.outputVelocities, with_timer)()

            log(self.outputContactForces, with_timer)()

            log(self.outputSolverInfos, with_timer)()

            log(simulation.nextStep, with_timer)()

            log(self._out.flush)()

            print ('')
            k += 1