Page 1 of 1

Non-Uniform Scaling 4.1

Posted: Wed Sep 02, 2020 9:56 am
by fmatari
Hi,

There seems to be an issue with the current output of inertia after non-uniform manual scaling. Our team has investigated this and come up with a suggested fix. Here is the issue posted on github. There is also a supporting document attached with more detail.

Identically scaling the gait2354 model in versions 3.3 and 4.1, comparison was performed for all segments. Scaled results were retrieved from the OpenSim GUI. Only difference in results was seen in inertia.

Looking into file body.cpp, we have been able to identify the problem in function scaleInertialProperties() line 218,

Code: Select all

SimTK::SymMat33 inertia = _inertia.asSymMat33();
For a reason unknown to us, the symmetric matrix is not allowing value assignments to specific elements.

(a) inertia *= ...
(b) inertia[0][0] = ...

Assignment type (a) works and (b) gets tolerated by the compiler but does not work. The suggested fix we have is to change the inertia variable type to a standard 3 X 3 matrix.

Code: Select all

SimTK::Mat33 inertia = _inertia.toMat33();
This replacement worked when tested with the command line scaling tool.

Hope this helps and please let us know if there is a better fix.

Best regards,
Fouad

Re: Non-Uniform Scaling 4.1

Posted: Wed Jun 16, 2021 12:04 pm
by frenkeld
We tracked down the reason for the behavior described in the comment above by Fouad Matari on broken non-uniform scaling.

Restating the issue, in the source file opensim-core-source\OpenSim\Simulation\SimbodyEngine\Body.cpp, in the function void Body::scaleInertialProperties(const SimTK::Vec3& scaleFactors, bool scaleMass), one finds that in the code section invoked when the three scale factors are not equal, assignments of the following kinds are used:

Code: Select all

            inertia[0][0] = 0.5 * get_mass() * rad_sqr;
            inertia[1][1] = get_mass() * ((length * length / 12.0) + 0.25 * rad_sqr);
            inertia[2][2] = get_mass() * ((length * length / 12.0) + 0.25 * rad_sqr);
and

Code: Select all

        inertia[0][1] *= ((scaleFactors[0] * scaleFactors[1]));
        inertia[0][2] *= ((scaleFactors[0] * scaleFactors[2]));
        inertia[1][0] *= ((scaleFactors[1] * scaleFactors[0]));
Both of them fail to modify the local object inertia allocated earlier in the statement

Code: Select all

   SimTK::SymMat33 inertia = _inertia.asSymMat33();
The reason is as follows. SymMat33 is a typedef for SymMat<3>. In that class, in SymMat.h, one finds

Code: Select all

    // These are slow for a symmetric matrix, requiring copying and
    // possibly floating point operations for conjugation.
    TRow operator[](int i) const {return row(i);}
in which the row() function is defined as

Code: Select all

    // Rows and columns have to be copied and Hermitian elements have to
    // be conjugated at a floating point cost. This isn't the best way
    // to work with a symmetric matrix.
    TRow row(int i) const {
...
        TRow rowi;
...
        return rowi;
    }
In this function a local object TRow rowi; is allocated, the row elements of inertia are copied to this local object, and it is then returned by the function. Thus when seeking inertia[j][k], the first [j] operation returns a TRow object whose elements point to newly allocated storage, not to anything in the original object inertia. The second [k] operator then returns a variable whose subsequent modification does not affect the original object inertia.

For this reason changing from SimTK::Mat33 to SimTK::SymMat33 in this function between OpenSim3.3 and OpenSim4.x broke the non-uniform scaling capability of the scaleInertialProperties() routine.

Re: Non-Uniform Scaling 4.1

Posted: Tue Jun 22, 2021 11:30 am
by aymanh
Thanks David for the find and the thorough investigation,

Your proposed fix is now available in development builds (on github) and will be available in the next public release.

All the best,
-Ayman