Repository: sympy/sympy

Issue:
ang_vel_in() could give a simpler result for body fixed angular velocities
If you set up a body fixed rotation and then call `.ang_vel_in()` I would expect the simplest form of the angular velocity to be returned, because we know apriori what the angular velocity is for any body fixed rotation.
```python
import sympy as sm
import sympy.physics.mechanics as me

psi, theta, phi = me.dynamicsymbols('psi, theta, varphi')

A = me.ReferenceFrame('A')
B = me.ReferenceFrame('B')
B.orient_body_fixed(A, (psi, theta, phi), 'ZXZ')
B.ang_vel_in(A)
```
gives:
```
⎛                         d                              d            ⎞       ⎛                         d                              d
⎜sin(θ(t))⋅sin(varphi(t))⋅──(ψ(t))        cos(varphi(t))⋅──(θ(t))     ⎟       ⎜sin(θ(t))⋅cos(varphi(t))⋅──(ψ(t))        sin(varphi(t))⋅──(
⎜                         dt                             dt           ⎟       ⎜                         dt                             dt
⎜───────────────────────────────── + ─────────────────────────────────⎟ b_x + ⎜───────────────────────────────── - ───────────────────────
⎜   2                 2                 2                 2           ⎟       ⎜   2                 2                 2                 2
⎝sin (varphi(t)) + cos (varphi(t))   sin (varphi(t)) + cos (varphi(t))⎠       ⎝sin (varphi(t)) + cos (varphi(t))   sin (varphi(t)) + cos (

          ⎞
θ(t))     ⎟
          ⎟       ⎛          d          d            ⎞
──────────⎟ b_y + ⎜cos(θ(t))⋅──(ψ(t)) + ──(varphi(t))⎟ b_z
          ⎟       ⎝          dt         dt           ⎠
varphi(t))⎠


```
We know the answer to this and I though that the code was supposed to return the simpler form for a lookup table. The simple form is:
```python
>>> B.ang_vel_in(A).simplify()
⎛                         d                         d       ⎞       ⎛                         d                         d       ⎞       ⎛
⎜sin(θ(t))⋅sin(varphi(t))⋅──(ψ(t)) + cos(varphi(t))⋅──(θ(t))⎟ b_x + ⎜sin(θ(t))⋅cos(varphi(t))⋅──(ψ(t)) - sin(varphi(t))⋅──(θ(t))⎟ b_y + ⎜c
⎝                         dt                        dt      ⎠       ⎝                         dt                        dt      ⎠       ⎝

         d          d            ⎞
os(θ(t))⋅──(ψ(t)) + ──(varphi(t))⎟ b_z
         dt         dt           ⎠


```
The manual calculation is:
```python
mnx = me.dot(B.y.express(A).dt(A), B.z)
mny = me.dot(B.z.express(A).dt(A), B.x)
mnz = me.dot(B.x.express(A).dt(A), B.y)
A_w_B = mnx*B.x + mny*B.y + mnz*B.z
A_w_B.simplify()
```
We have this function:
https://github.com/sympy/sympy/blob/master/sympy/physics/vector/functions.py#L227
which has this big lookup table but I don't think it is used.

 

Discussion:

Patch:
diff --git a/sympy/physics/vector/frame.py b/sympy/physics/vector/frame.py
index 899832f30f..d384feff32 100644
--- a/sympy/physics/vector/frame.py
+++ b/sympy/physics/vector/frame.py
@@ -892,6 +892,13 @@ def orient_body_fixed(self, parent, angles, rotation_order):
             u2 = expand(td[u2])
             u3 = expand(td[u3])
             wvec = u1 * self.x + u2 * self.y + u3 * self.z
+            # NOTE : SymPy 1.7 removed the call to simplify() that occured
+            # inside the solve() function, so this restores the pre-1.7
+            # behavior. See:
+            # https://github.com/sympy/sympy/issues/23140
+            # and
+            # https://github.com/sympy/sympy/issues/23130
+            wvec = wvec.simplify()
         except (CoercionFailed, AssertionError):
             wvec = self._w_diff_dcm(parent)
         self._ang_vel_dict.update({parent: wvec})


Test patch:
diff --git a/sympy/physics/vector/tests/test_frame.py b/sympy/physics/vector/tests/test_frame.py
index 715d6f6ffe..d3e2fe4a76 100644
--- a/sympy/physics/vector/tests/test_frame.py
+++ b/sympy/physics/vector/tests/test_frame.py
@@ -473,6 +473,27 @@ def test_orient_body():
     B.orient_body_fixed(A, (1,1,0), 'XYX')
     assert B.dcm(A) == Matrix([[cos(1), sin(1)**2, -sin(1)*cos(1)], [0, cos(1), sin(1)], [sin(1), -sin(1)*cos(1), cos(1)**2]])
 
+
+def test_orient_body_simple_ang_vel():
+    """orient_body_fixed() uses kinematic_equations() internally and solves
+    those equations for the measure numbers of the angular velocity. This test
+    ensures that the simplest form of that linear system solution is returned,
+    thus the == for the expression comparison."""
+
+    psi, theta, phi = dynamicsymbols('psi, theta, varphi')
+    t = dynamicsymbols._t
+    A = ReferenceFrame('A')
+    B = ReferenceFrame('B')
+    B.orient_body_fixed(A, (psi, theta, phi), 'ZXZ')
+    A_w_B = B.ang_vel_in(A)
+    assert A_w_B.args[0][1] == B
+    assert A_w_B.args[0][0][0] == (sin(theta)*sin(phi)*psi.diff(t) +
+                                   cos(phi)*theta.diff(t))
+    assert A_w_B.args[0][0][1] == (sin(theta)*cos(phi)*psi.diff(t) -
+                                   sin(phi)*theta.diff(t))
+    assert A_w_B.args[0][0][2] == cos(theta)*psi.diff(t) + phi.diff(t)
+
+
 def test_orient_space():
     A = ReferenceFrame('A')
     B = ReferenceFrame('B')