`mechanics` provides functionality for deriving equations of motion
using Lagrange’s method.
This document will describe Lagrange’s method as used in this module, but not
how the equations are actually derived.

In `mechanics` we are assuming there are 3 basic sets of equations needed
to describe a system; the constraint equations, the time differentiated
constraint equations and the dynamic equations.

\[\begin{split}\mathbf{m_{c}}(q, t) \dot{q} + \mathbf{f_{c}}(q, t) &= 0\\
\mathbf{m_{dc}}(\dot{q}, q, t) \ddot{q} + \mathbf{f_{dc}}(\dot{q}, q, t) &= 0\\
\mathbf{m_d}(\dot{q}, q, t) \ddot{q} + \mathbf{\Lambda_c}(q, t)
\lambda + \mathbf{f_d}(\dot{q}, q, t) &= 0\\\end{split}\]

In this module, the expressions formed by using Lagrange’s equations of the second kind are rearranged into the following form:

\(\mathbf{M}(q, t) x = \mathbf{f}(q, \dot{q}, t)\)

where in the case of a system without constraints:

\(x = \ddot{q}\)

For a constrained system with \(n\) generalized speeds and \(m\) constraints, we will get n - m equations. The mass-matrix/forcing equations are then augmented in the following fashion:

\[\begin{split}x = \begin{bmatrix} \ddot{q} \\ \lambda \end{bmatrix} \\
\mathbf{M}(q, t) &= \begin{bmatrix} \mathbf{m_d}(q, t) &
\mathbf{\Lambda_c}(q, t) \end{bmatrix}\\
\mathbf{F}(\dot{q}, q, t) &= \begin{bmatrix} \mathbf{f_d}(q, \dot{q}, t)
\end{bmatrix}\\\end{split}\]

The formulation of the equations of motion in `mechanics` using
Lagrange’s Method starts with the creation of generalized coordinates and a
Lagrangian. The Lagrangian can either be created with the `Lagrangian`
function or can be a user supplied function. In this case we will supply the
Lagrangian.

```
>>> from sympy.physics.mechanics import *
>>> q1, q2 = dynamicsymbols('q1 q2')
>>> q1d, q2d = dynamicsymbols('q1 q2', 1)
>>> L = q1d**2 + q2d**2
```

To formulate the equations of motion we create a `LagrangesMethod`
object. The Lagrangian and generalized coordinates need to be supplied upon
initialization.

```
>>> LM = LagrangesMethod(L, [q1, q2])
```

With that the equations of motion can be formed.

```
>>> mechanics_printing(pretty_print=False)
>>> LM.form_lagranges_equations()
Matrix([
[2*q1''],
[2*q2'']])
```

It is possible to obtain the mass matrix and the forcing vector.

```
>>> LM.mass_matrix
Matrix([
[2, 0],
[0, 2]])
>>> LM.forcing
Matrix([
[0],
[0]])
```

If there are any holonomic or non-holonomic constraints, they must be supplied
as keyword arguments (`hol_coneqs` and `nonhol_coneqs` respectively) in a
list of expressions which are equal to zero. Modifying the example above, the
equations of motion can then be generated:

```
>>> LM = LagrangesMethod(L, [q1, q2], hol_coneqs=[q1 - q2])
```

When the equations of motion are generated in this case, the Lagrange
multipliers are introduced; they are represented by `lam1` in this case. In
general, there will be as many multipliers as there are constraint equations.

```
>>> LM.form_lagranges_equations()
Matrix([
[ lam1 + 2*q1''],
[-lam1 + 2*q2'']])
```

Also in the case of systems with constraints, the ‘full’ mass matrix is augmented by the \(k_{dc}(q, t)\) matrix, and the forcing vector by the \(f_{dc}(q, \dot{q}, t)\) vector. The ‘full’ mass matrix is of size (2n + o) x (2n + o), i.e. it’s a square matrix.

```
>>> LM.mass_matrix_full
Matrix([
[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 2, 0, -1],
[0, 0, 0, 2, 1],
[0, 0, 1, -1, 0]])
>>> LM.forcing_full
Matrix([
[q1'],
[q2'],
[ 0],
[ 0],
[ 0]])
```

If there are any non-conservative forces or moments acting on the system,
they must also be supplied as keyword arguments in a list of 2-tuples of the
form `(Point, Vector)` or `(ReferenceFrame, Vector)` where the `Vector`
represents the non-conservative forces and torques. Along with this 2-tuple,
the inertial frame must also be specified as a keyword argument. This is shown
below by modifying the example above:

```
>>> N = ReferenceFrame('N')
>>> P = Point('P')
>>> P.set_vel(N, q1d * N.x)
>>> FL = [(P, 7 * N.x)]
>>> LM = LagrangesMethod(L, [q1, q2], forcelist=FL, frame=N)
>>> LM.form_lagranges_equations()
Matrix([
[2*q1'' - 7],
[ 2*q2'']])
```

Exploration of the provided examples is encouraged in order to gain more
understanding of the `LagrangesMethod` object.