Example #1
0
def validateInertiaData(obj, *args, adjust=False):
    """Validates an inertia dictionary or object.
    
    This checks for the *inertia* and *mass* values in the dictionary (*inertial/inertia* and
    *inertial/mass* for an object respectively).
    
    Also, the inertia values are checked to be positive definite (for diagonal, determinant and
    eigenvalues).
    
    If adjust is set, values are adjusted/fixed for the returned dict/object. E.g. this sets a
    negative mass to 1e-3.

    Args:
      obj(dict/bpy.types.Object): inertia dictionary or object to validate
      *args: other arguments
      adjust: if True, bad values will be fixed/complemented (Default value = False)

    Returns:
      tuple: list of :class:`ValidateMessage`\ s and the fixed dictionary/object

    """
    from phobos.model.inertia import inertiaListToMatrix, inertiaMatrixToList
    from phobos.utils.io import getExpSettings
    import numpy

    errors = []

    expsetting = 10**(-getExpSettings().decimalPlaces)

    # check dictionary parameters (most of the time pre object creation)
    if isinstance(obj, dict):
        missing = []
        if 'inertia' not in obj:
            missing.append('inertia')

        if 'mass' not in obj:
            missing.append('mass')

        if missing:
            errors.append(
                ValidateMessage(
                    "Inertia dictionary not fully defined!",
                    'WARNING',
                    None,
                    None,
                    {
                        'log_info':
                        "Missing: " +
                        ' '.join(["'{0}'".format(miss) for miss in missing]) +
                        " Set to default 1e-3."
                    },
                ))

            if 'inertia' in missing:
                obj['inertia'] = (1e-3, 0., 0., 1e-3, 0., 1e-3)
            if 'mass' in missing:
                obj['mass'] = 1e-3

        inertia = obj['inertia']
        mass = obj['mass']
    # check existing object properties
    elif isinstance(obj, bpy.types.Object):
        if 'inertial/inertia' not in obj:
            errors.append(
                ValidateMessage(
                    "Inertia not defined!",
                    'WARNING',
                    obj,
                    'phobos.generate_inertial_objects',
                    {'log_info': "Set to default 1e-3."},
                ))
            obj['inertial/inertia'] = (1e-3, 0., 0., 1e-3, 0., 1e-3)

        if 'inertial/mass' not in obj:
            errors.append(
                ValidateMessage(
                    "Mass is not defined!",
                    'WARNING',
                    obj,
                    'phobos.generate_inertial_objects',
                    {'log_info': "Set to default 1e-3."},
                ))
            obj['inertial/mass'] = 1e-3
        inertia = obj['inertial/inertia']
        mass = obj['inertial/mass']

    # Check inertia vector for various properties, round to export precision
    inertia = numpy.around(numpy.array(inertiaListToMatrix(inertia)),
                           decimals=getExpSettings().decimalPlaces)
    if any(element <= expsetting for element in inertia.diagonal()):
        errors.append(
            ValidateMessage(
                "Negative semidefinite main diagonal in inertia data!",
                'WARNING',
                None if isinstance(obj, dict) else obj,
                None,
                {'log_info': "Diagonal: " + str(inertia.diagonal())},
            ))

    # Calculate the determinant if consistent, quick check
    if numpy.linalg.det(inertia) <= expsetting:
        errors.append(
            ValidateMessage(
                "Negative semidefinite determinant in inertia data! Checking singular values.",
                'WARNING',
                None if isinstance(obj, dict) else obj,
                None,
                {'log_info': "Determinant: " + str(numpy.linalg.det(inertia))},
            ))

        # Calculate the eigenvalues if not consistent
        if any(element <= expsetting
               for element in numpy.linalg.eigvals(inertia)):
            # Apply singular value decomposition and correct the values
            S, V = numpy.linalg.eig(inertia)
            S[S <= expsetting] = expsetting
            inertia = V.dot(numpy.diag(S).dot(V.T))
            errors.append(
                ValidateMessage(
                    "Negative semidefinite eigenvalues in inertia data!",
                    'WARNING',
                    None if isinstance(obj, dict) else obj,
                    None,
                    {
                        'log_info':
                        "Eigenvalues: " + str(numpy.linalg.eigvals(inertia))
                    },
                ))

    if mass <= 0.:
        errors.append(
            ValidateMessage(
                "Mass is {}!".format('zero' if mass == 0. else 'negative'),
                'WARNING',
                None if isinstance(obj, dict) else obj,
                None,
                {} if not adjust else {'log_info': "Adjusted to 1e-3."},
            ))
        mass = expsetting

    inertia = inertiaMatrixToList(inertia)

    if adjust and isinstance(obj, bpy.types.Object):
        obj['inertial/inertia'] = inertia
        obj['inertial/mass'] = mass
    elif adjust:
        obj['inertia'] = inertia
        obj['mass'] = mass

    return errors, obj
Example #2
0
def validateInertiaData(obj, *args, adjust=False):
    """Validates an inertia dictionary or object.
    
    This checks for the *inertia* and *mass* values in the dictionary (*inertial/inertia* and
    *inertial/mass* for an object respectively).
    
    Also, the inertia values are checked to be positive definite (for diagonal, determinant and
    eigenvalues).
    
    If adjust is set, values are adjusted/fixed for the returned dict/object. E.g. this sets a
    negative mass to 1e-3.

    Args:
      obj(dict/bpy.types.Object): inertia dictionary or object to validate
      *args: other arguments
      adjust: if True, bad values will be fixed/complemented (Default value = False)

    Returns:
      tuple: list of :class:`ValidateMessage`\ s and the fixed dictionary/object

    """
    from phobos.model.inertia import inertiaListToMatrix, inertiaMatrixToList
    from phobos.utils.io import getExpSettings
    import numpy

    errors = []

    # check dictionary parameters (most of the time pre object creation)
    if isinstance(obj, dict):
        missing = []
        if 'inertia' not in obj:
            missing.append('inertia')

        if 'mass' not in obj:
            missing.append('mass')

        if missing:
            errors.append(
                ValidateMessage(
                    "Inertia dictionary not fully defined!",
                    'WARNING',
                    None,
                    None,
                    {
                        'log_info': "Missing: "
                        + ' '.join(["'{0}'".format(miss) for miss in missing])
                        + " Set to default 1e-3."
                    },
                )
            )

            if 'inertia' in missing:
                obj['inertia'] = (1e-3, 0., 0., 1e-3, 0., 1e-3)
            if 'mass' in missing:
                obj['mass'] = 1e-3

        inertia = obj['inertia']
        mass = obj['mass']
    # check existing object properties
    elif isinstance(obj, bpy.types.Object):
        if 'inertial/inertia' not in obj:
            errors.append(
                ValidateMessage(
                    "Inertia not defined!",
                    'WARNING',
                    obj,
                    'phobos.generate_inertial_objects',
                    {'log_info': "Set to default 1e-3."},
                )
            )
            obj['inertial/inertia'] = (1e-3, 0., 0., 1e-3, 0., 1e-3)

        if 'inertial/mass' not in obj:
            errors.append(
                ValidateMessage(
                    "Mass is not defined!",
                    'WARNING',
                    obj,
                    'phobos.generate_inertial_objects',
                    {'log_info': "Set to default 1e-3."},
                )
            )
            obj['inertial/mass'] = 1e-3
        inertia = obj['inertial/inertia']
        mass = obj['inertial/mass']

    # Check inertia vector for various properties, round to export precision
    inertia = numpy.around(
        numpy.array(inertiaListToMatrix(inertia)), decimals=getExpSettings().decimalPlaces
    )
    if any(element <= 0.0 for element in inertia.diagonal()):
        errors.append(
            ValidateMessage(
                "Negative semidefinite main diagonal in inertia data!",
                'WARNING',
                None if isinstance(obj, dict) else obj,
                None,
                {'log_info': "Diagonal: " + str(inertia.diagonal())},
            )
        )

    # Calculate the determinant if consistent, quick check
    if numpy.linalg.det(inertia) <= 0.0:
        errors.append(
            ValidateMessage(
                "Negative semidefinite determinant in inertia data! Checking singular values.",
                'WARNING',
                None if isinstance(obj, dict) else obj,
                None,
                {'log_info': "Determinant: " + str(numpy.linalg.det(inertia))},
            )
        )

        # Calculate the eigenvalues if not consistent
        if any(element <= 0.0 for element in numpy.linalg.eigvals(inertia)):
            # Apply singular value decomposition and correct the values
            S, V = numpy.linalg.eig(inertia)
            S[S <= 0.0] = 1e-3
            inertia = V.dot(numpy.diag(S).dot(V.T))
            errors.append(
                ValidateMessage(
                    "Negative semidefinite eigenvalues in inertia data!",
                    'WARNING',
                    None if isinstance(obj, dict) else obj,
                    None,
                    {'log_info': "Eigenvalues: " + str(numpy.linalg.eigvals(inertia))},
                )
            )

    if mass <= 0.:
        errors.append(
            ValidateMessage(
                "Mass is {}!".format('zero' if mass == 0. else 'negative'),
                'WARNING',
                None if isinstance(obj, dict) else obj,
                None,
                {} if not adjust else {'log_info': "Adjusted to 1e-3."},
            )
        )
        mass = 1e-3

    inertia = inertiaMatrixToList(inertia)

    if adjust and isinstance(obj, bpy.types.Object):
        obj['inertial/inertia'] = inertia
        obj['inertial/mass'] = mass
    elif adjust:
        obj['inertia'] = inertia
        obj['mass'] = mass

    return errors, obj