# 21. `inertia` — Compute inertia related quantities of geometrical models.

Inertia related quantities of a geometrical model comprise: the total mass, the center of mass, the inertia tensor, the principal axes of inertia.

This module defines some classes to store the inertia data:

- `Tensor`: a general second order tensor
- `Inertia`: a specialized second order tensor for inertia data

This module also provides the basic functions to compute the inertia data of collections of simple geometric data: points, lines, triangles, tetrahedrons.

The prefered way to compute inertia data of a geometric model is through the `Geometry.inertia()` methods.

## 21.1. Classes defined in module inertia

class `inertia.``Tensor`

  A second order symmetric(!) tensor in 3D vector space.

  This is a new class under design. Only use for development!

  The Tensor class provides conversion between full matrix (3,3) shape and contracted vector (6,) shape. It can e.g. be used to store an inertia tensor or a stress or strain tensor. It provides methods to transform the tensor to other (cartesian) axes.

  Parameters:

  - data: array-like (float) of shape (3,3) or (6,)
  - symmetric: bool. If True (default), the tensor is forced to be symmetric by averaging the off-diagonal elements.
  - cs: CoordSys. The coordinate system of the tensor.

  Properties: a Tensor T has the following properties:

  - T.xx, T.xy, T.xz, T.yx, T.yy, T.yz, T.zx, T.zy, T.zz: aliases for the nine components of the tensor
  - T.contracted: the (6,) shaped contracted array with independent values of the tensor
  - T.tensor: the full tensor as an (3,3) array

  Discussion:

  - inertia and stres/strain tensors transform in the same way on rotations of axes. But differently on translations! Should we therefore store the purpose of the tensor??
    - Propose to leave it to the user to know what he is doing.
    - Propose to have a separate class Inertia derived from Tensor, which implements computing the inertia tensor and translation.
  - should we allow non-symmetrical tensors? Then what with principal?
    - Propose to silently allow non-symm. Result of functions is what it is. Again, suppose the user knows what he is doing.

  Example

      >>> t = Tensor([1,2,3,4,5,6])
      >>> print(t)
      [[ 1. 6. 5.]
       [ 6. 2. 4.]
       [ 5. 4. 3.]]
      >>> print(t.contracted)
      [ 1. 2. 3. 4. 5. 6.]
      >>> s = Tensor(t)
      >>> print(s)
      [[ 1. 6. 5.]
       [ 6. 2. 4.]
       [ 5. 4. 3.]]

  `contracted`

    Returned the symmetric tensor data as a numpy array with shape (6,)

  `tensor`

    Returned the tensor data as a numpy array with shape (3,3)

  `sym`

    Return the symmetric part of the tensor.

  `asym`

    Return the antisymmetric part of the tensor.

  `principal`(sort=True, right_handed=True)

    Returns the principal values and axes of the inertia tensor.

    Parameters:

    - sort: bool. If True (default), the return values are sorted in order of decreasing principal values. Otherwise they are unsorted.
    - right_handed: bool. If True (default), the returned axis vectors are guaranteed to form a right-handed coordinate system. Otherwise, lef-handed systems may result)

    Returns a tuple (prin,axes) where

    - prin: is a (3,) array with the principal values,
    - axes: is a (3,3) array with the rotation matrix that rotates the global axes to the principal axes. This also means that the rows of axes are the unit vectors along the principal directions.

    Example:

        >>> t = Tensor([-19., 4.6, -8.3, 11.8, 6.45, -4.7 ])
        >>> p,a = t.principal()
        >>> print(p)
        [ 11.62  -9.   -25.32]
        >>> print(a)
        [[-0.03  0.86  0.5 ]
         [-0.62  0.38 -0.69]
         [-0.78 -0.33  0.53]]

  `rotate`(rot)

    Transform the tensor on coordinate system rotation.

    Note: for an inertia tensor, the inertia should have been computed around axes through the center of mass. See also translate.

    Example:

        >>> t = Tensor([-19., 4.6, -8.3, 11.8, 6.45, -4.7 ])
        >>> p,a = t.principal()
        >>> print(t.rotate(np.linalg.linalg.inv(a)))
        [[ 11.62   0.     0.  ]
         [ -0.    -9.    -0.  ]
         [  0.    -0.   -25.32]]

class `inertia.``Inertia`

  A class for storing the inertia tensor of an array of points.

  Parameters:

  - X: a Coords with shape (npoints,3). Shapes (…,3) are accepted but will be reshaped to (npoints,3).

  - mass: optional, (npoints,) float array with the mass of the points. If omitted, all points have mass 1.

    The result is a tuple of two float arrays:

    - the center of gravity: shape (3,)
    - the inertia tensor: shape (6,) with the following values (in order): Ixx, Iyy, Izz, Iyz, Izx, Ixy

  Example:

      >>> from .elements import Tet4
      >>> X = Tet4.vertices
      >>> print(X)
      [[ 0. 0. 0.]
       [ 1. 0. 0.]
       [ 0. 1. 0.]
       [ 0. 0. 1.]]
      >>> I = X.inertia()
      >>> print(I)
      [[ 1.5  0.25 0.25]
       [ 0.25 1.5  0.25]
       [ 0.25 0.25 1.5 ]]
      >>> print(I.ctr)
      [ 0.25 0.25 0.25]
      >>> print(I.mass)
      4.0
      >>> print(I.translate(-I.ctr))
      [[ 2. 0. 0.]
       [ 0. 2. 0.]
       [ 0. 0. 2.]]

  `translate`(trl, toG=False)

    Return the inertia tensor around axes translated over vector trl.

    Parameters:

    - trl: arraylike (3,). Distance vector from the center of mass to the new reference point.
    - toG: bool. If False (default) the inertia tensor is translated to the the new reference point, otherwise it will be translated to its center of mass
  `translateTo`(ref, toG=False)

    Return the inertia tensor around axes translated to the reference point ref.

    Parameters:

    - ref: arraylike (3,). The new reference point coordinates.
    - toG: bool. If False (default) the inertia tensor is translated to the the new reference point, otherwise it will be translated to its center of mass
  `toCS`(cs)

    Transform the coordinates to another CoordSys.

## 21.2. Functions defined in module inertia

`inertia.``point_inertia`(X, mass=None, center_only=False)

  Compute the total mass, center of mass and inertia tensor mass points.

  Parameters:

  - X: a Coords with shape (npoints,3). Shapes (…,3) are accepted but will be reshaped to (npoints,3).
  - mass: optional, (npoints,) float array with the mass of the points. If omitted, all points have mass 1.
  - center_only: bool: if True, only returns the total mass and center of mass.

  Returns a tuple (M,C,I) where M is the total mass of all points, C is the center of mass, and I is the inertia tensor in the central coordinate system, i.e. a coordinate system with axes paralle to the global axes but origin at the (computed) center of mass. If center_only is True, returns the tuple (M,C) only. On large models this is more effective in case you do not need the inertia tensor.

`inertia.``surface_volume`(x, pt=None)

  Return the volume inside a 3-plex Formex.

  - x: an (ntri,3,3) shaped float array, representing ntri triangles.
  - pt: a point in space. If unspecified, it is taken equal to the origin of the global coordinate system ([0.,0.,0.]).

  Returns an (ntri) shaped array with the volume of the tetrahedrons formed by the triangles and the point pt. Triangles with an outer normal pointing away from pt will generate positive tetrahral volumes, while triangles having pt at the side of their positive normal will generate negative volumes. In any case, if x represents a closed surface, the algebraic sum of all the volumes is the total volume inside the surface.

`inertia.``surface_volume_inertia`(x, center_only=False)

  Return the inertia of the volume inside a 3-plex Formex.

  - x: an (ntri,3,3) shaped float array, representing ntri triangles.

  This uses the same algorithm as tetrahedral_inertia using [0.,0.,0.] as the 4-th point for each tetrahedron.

  Returns a tuple (V,C,I) where V is the total volume, C is the center of mass (3,) and I is the inertia tensor (6,) of the tetrahedral model.

  Example:

      >>> from .simple import sphere
      >>> S = sphere(4).toFormex()
      >>> V,C,I = surface_volume_inertia(S.coords)
      >>> print(V,C,I)
      4.04701 [-0. -0. -0.] [ 1.58  1.58  1.58 -0.    0.    0.  ]

`inertia.``tetrahedral_volume`(x)

  Compute the volume of tetrahedrons.

  - x: an (ntet,4,3) shaped float array, representing ntet tetrahedrons.

  Returns an (ntet,) shaped array with the volume of the tetrahedrons. Depending on the ordering of the points, this volume may be positive or negative. It will be positive if point 4 is on the side of the positive normal formed by the first 3 points.

`inertia.``tetrahedral_inertia`(x, density=None, center_only=False)

  Return the inertia of the volume of a 4-plex Formex.

  Parameters:

  - x: an (ntet,4,3) shaped float array, representing ntet tetrahedrons.
  - density: optional mass density (ntet,) per tetrahedron
  - center_only: bool. If True, returns only the total volume, total mass and center of gravity. This may be used on large models when only these quantities are required.

  Returns a tuple (V,M,C,I) where V is the total volume, M is the total mass, C is the center of mass (3,) and I is the inertia tensor (6,) of the tetrahedral model.

  Formulas for inertia were based on F. Tonon, J. Math & Stat, 1(1):8-11,2005

  Example:

      >>> x = Coords([
      ...     [  8.33220, -11.86875,  0.93355 ],
      ...     [  0.75523,   5.00000, 16.37072 ],
      ...     [ 52.61236,   5.00000, -5.38580 ],
      ...     [  2.000000,  5.00000,  3.00000 ],
      ...     ])
      >>> F = Formex([x])
      >>> print(tetrahedral_center(F.coords))
      [ 15.92  0.78  3.73]
      >>> print(tetrahedral_volume(F.coords))
      [ 1873.23]
      >>> print(*tetrahedral_inertia(F.coords))
      1873.23 1873.23 [ 15.92  0.78  3.73] [ 43520.32 194711.28 191168.77   4417.66 -46343.16  11996.2 ]

`inertia.``tetrahedral_center`(x, density=None)

  Compute the center of mass of a collection of tetrahedrons.

  - x: an (ntet,4,3) shaped float array, representing ntet tetrahedrons.
  - density: optional mass density (ntet,) per tetrahedron. Default 1.

  Returns a (3,) shaped array with the center of mass.

`inertia.``inertia`(X, mass=None, center_only=False)

  Compute the total mass, center of mass and inertia tensor mass points.

  Parameters:

  - X: a Coords with shape (npoints,3). Shapes (…,3) are accepted but will be reshaped to (npoints,3).
  - mass: optional, (npoints,) float array with the mass of the points. If omitted, all points have mass 1.
  - center_only: bool: if True, only returns the total mass and center of mass.

  Returns a tuple (M,C,I) where M is the total mass of all points, C is the center of mass, and I is the inertia tensor in the central coordinate system, i.e. a coordinate system with axes paralle to the global axes but origin at the (computed) center of mass. If center_only is True, returns the tuple (M,C) only. On large models this is more effective in case you do not need the inertia tensor.