Repository: sympy/sympy

Issue:
physics.vector.Vector should have atoms and free_symbols
It would be nice to be able to call `free_symbols` on a physics vector and it works as it does with any expression. It would also be nice if `find_dynamicsymbols` worked with vectors. A current hack is to convert to a matrix then use these functions.

Discussion:

Patch:
diff --git a/sympy/physics/mechanics/functions.py b/sympy/physics/mechanics/functions.py
index 2aea3bb35a..5d7806b212 100644
--- a/sympy/physics/mechanics/functions.py
+++ b/sympy/physics/mechanics/functions.py
@@ -399,20 +399,44 @@ def Lagrangian(frame, *body):
     return kinetic_energy(frame, *body) - potential_energy(*body)
 
 
-def find_dynamicsymbols(expression, exclude=None):
+def find_dynamicsymbols(expression, exclude=None, reference_frame=None):
     """Find all dynamicsymbols in expression.
 
+    If the optional ``exclude`` kwarg is used, only dynamicsymbols
+    not in the iterable ``exclude`` are returned.
+    If we intend to apply this function on a vector, the optional
+    ''reference_frame'' is also used to inform about the corresponding frame
+    with respect to which the dynamic symbols of the given vector is to be
+    determined.
+
+    Parameters
+    ==========
+
+    expression : sympy expression
+
+    exclude : iterable of dynamicsymbols, optional
+
+    reference_frame : ReferenceFrame, optional
+        The frame with respect to which the dynamic symbols of the
+        given vector is to be determined.
+
+    Examples
+    ========
+
     >>> from sympy.physics.mechanics import dynamicsymbols, find_dynamicsymbols
+    >>> from sympy.physics.mechanics import ReferenceFrame
     >>> x, y = dynamicsymbols('x, y')
     >>> expr = x + x.diff()*y
     >>> find_dynamicsymbols(expr)
     {x(t), y(t), Derivative(x(t), t)}
-
-    If the optional ``exclude`` kwarg is used, only dynamicsymbols
-    not in the iterable ``exclude`` are returned.
-
-    >>> find_dynamicsymbols(expr, [x, y])
+    >>> find_dynamicsymbols(expr, exclude=[x, y])
     {Derivative(x(t), t)}
+    >>> a, b, c = dynamicsymbols('a, b, c')
+    >>> A = ReferenceFrame('A')
+    >>> v = a * A.x + b * A.y + c * A.z
+    >>> find_dynamicsymbols(v, reference_frame=A)
+    {a(t), b(t), c(t)}
+
     """
     t_set = {dynamicsymbols._t}
     if exclude:
@@ -422,6 +446,12 @@ def find_dynamicsymbols(expression, exclude=None):
             raise TypeError("exclude kwarg must be iterable")
     else:
         exclude_set = set()
+    if isinstance(expression, Vector):
+        if reference_frame is None:
+            raise ValueError("You must provide reference_frame when passing a "
+                             "vector expression, got %s." % reference_frame)
+        else:
+            expression = expression.to_matrix(reference_frame)
     return set([i for i in expression.atoms(AppliedUndef, Derivative) if
             i.free_symbols == t_set]) - exclude_set
 
diff --git a/sympy/physics/units/definitions.py b/sympy/physics/units/definitions.py
index 89ccc3184a..49ed0eb104 100644
--- a/sympy/physics/units/definitions.py
+++ b/sympy/physics/units/definitions.py
@@ -149,7 +149,7 @@
 # vacuum impedance:
 Z0 = vacuum_impedance = Quantity("vacuum_impedance", impedance, u0 * c)
 # Coulomb's constant:
-coulomb_constant = coulombs_constant = electric_force_constant = Quantity("coulomb_constant", force*length**2/charge**2, 1/(4*pi*vacuum_permittivity), abbrev="k_e")
+coulomb_constant = electric_force_constant = Quantity("coulomb_constant", force*length**2/charge**2, 1/(4*pi*vacuum_permittivity), abbrev="k_e")
 
 atmosphere = atmospheres = atm = Quantity("atmosphere", pressure, 101325 * pascal, abbrev="atm")
 
diff --git a/sympy/physics/vector/vector.py b/sympy/physics/vector/vector.py
index 85d165d595..ee7abcac3f 100644
--- a/sympy/physics/vector/vector.py
+++ b/sympy/physics/vector/vector.py
@@ -695,6 +695,22 @@ def applyfunc(self, f):
             d[v[1]] = v[0].applyfunc(f)
         return Vector(d)
 
+    def free_symbols(self, reference_frame):
+        """
+        Returns the free symbols in the measure numbers of the vector
+        expressed in the given reference frame.
+
+        Parameter
+        =========
+
+        reference_frame : ReferenceFrame
+            The frame with respect to which the free symbols of the
+            given vector is to be determined.
+
+        """
+
+        return self.to_matrix(reference_frame).free_symbols
+
 
 class VectorTypeError(TypeError):
 


Test patch:
diff --git a/sympy/physics/mechanics/tests/test_functions.py b/sympy/physics/mechanics/tests/test_functions.py
index 548e4f7168..db8d3ca149 100644
--- a/sympy/physics/mechanics/tests/test_functions.py
+++ b/sympy/physics/mechanics/tests/test_functions.py
@@ -6,6 +6,8 @@
                                      kinetic_energy, linear_momentum,
                                      outer, potential_energy, msubs,
                                      find_dynamicsymbols)
+from sympy.physics.vector.vector import Vector
+from sympy.utilities.pytest import raises
 
 Vector.simp = True
 q1, q2, q3, q4, q5 = symbols('q1 q2 q3 q4 q5')
@@ -165,6 +167,14 @@ def test_find_dynamicsymbols():
     sol = {x, y.diff(), y, x.diff().diff(), z, z.diff()}
     assert find_dynamicsymbols(expr) == sol
     # Test finding all but those in sym_list
-    exclude = [x, y, z]
+    exclude_list = [x, y, z]
     sol = {y.diff(), x.diff().diff(), z.diff()}
-    assert find_dynamicsymbols(expr, exclude) == sol
+    assert find_dynamicsymbols(expr, exclude=exclude_list) == sol
+    # Test finding all dynamicsymbols in a vector with a given reference frame
+    d, e, f = dynamicsymbols('d, e, f')
+    A = ReferenceFrame('A')
+    v = d * A.x + e * A.y + f * A.z
+    sol = {d, e, f}
+    assert find_dynamicsymbols(v, reference_frame=A) == sol
+    # Test if a ValueError is raised on supplying only a vector as input
+    raises(ValueError, lambda: find_dynamicsymbols(v))
diff --git a/sympy/physics/vector/tests/test_vector.py b/sympy/physics/vector/tests/test_vector.py
index 5079c8fac4..828a25f3c4 100644
--- a/sympy/physics/vector/tests/test_vector.py
+++ b/sympy/physics/vector/tests/test_vector.py
@@ -54,6 +54,10 @@ def test_Vector():
     assert v1.separate() == {A: v1}
     assert v5.separate() == {A: x*A.x + y*A.y, B: z*B.z}
 
+    #Test the free_symbols property
+    v6 = x*A.x + y*A.y + z*A.z
+    assert v6.free_symbols(A) == {x,y,z}
+
 
 def test_Vector_diffs():
     q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4')