switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
__all__ = [
|
||||
'CoordinateSym', 'ReferenceFrame',
|
||||
|
||||
'Dyadic',
|
||||
|
||||
'Vector',
|
||||
|
||||
'Point',
|
||||
|
||||
'cross', 'dot', 'express', 'time_derivative', 'outer',
|
||||
'kinematic_equations', 'get_motion_params', 'partial_velocity',
|
||||
'dynamicsymbols',
|
||||
|
||||
'vprint', 'vsstrrepr', 'vsprint', 'vpprint', 'vlatex', 'init_vprinting',
|
||||
|
||||
'curl', 'divergence', 'gradient', 'is_conservative', 'is_solenoidal',
|
||||
'scalar_potential', 'scalar_potential_difference',
|
||||
|
||||
]
|
||||
from .frame import CoordinateSym, ReferenceFrame
|
||||
|
||||
from .dyadic import Dyadic
|
||||
|
||||
from .vector import Vector
|
||||
|
||||
from .point import Point
|
||||
|
||||
from .functions import (cross, dot, express, time_derivative, outer,
|
||||
kinematic_equations, get_motion_params, partial_velocity,
|
||||
dynamicsymbols)
|
||||
|
||||
from .printing import (vprint, vsstrrepr, vsprint, vpprint, vlatex,
|
||||
init_vprinting)
|
||||
|
||||
from .fieldfunctions import (curl, divergence, gradient, is_conservative,
|
||||
is_solenoidal, scalar_potential, scalar_potential_difference)
|
||||
@@ -0,0 +1,545 @@
|
||||
from sympy import sympify, Add, ImmutableMatrix as Matrix
|
||||
from sympy.core.evalf import EvalfMixin
|
||||
from sympy.printing.defaults import Printable
|
||||
|
||||
from mpmath.libmp.libmpf import prec_to_dps
|
||||
|
||||
|
||||
__all__ = ['Dyadic']
|
||||
|
||||
|
||||
class Dyadic(Printable, EvalfMixin):
|
||||
"""A Dyadic object.
|
||||
|
||||
See:
|
||||
https://en.wikipedia.org/wiki/Dyadic_tensor
|
||||
Kane, T., Levinson, D. Dynamics Theory and Applications. 1985 McGraw-Hill
|
||||
|
||||
A more powerful way to represent a rigid body's inertia. While it is more
|
||||
complex, by choosing Dyadic components to be in body fixed basis vectors,
|
||||
the resulting matrix is equivalent to the inertia tensor.
|
||||
|
||||
"""
|
||||
|
||||
is_number = False
|
||||
|
||||
def __init__(self, inlist):
|
||||
"""
|
||||
Just like Vector's init, you should not call this unless creating a
|
||||
zero dyadic.
|
||||
|
||||
zd = Dyadic(0)
|
||||
|
||||
Stores a Dyadic as a list of lists; the inner list has the measure
|
||||
number and the two unit vectors; the outerlist holds each unique
|
||||
unit vector pair.
|
||||
|
||||
"""
|
||||
|
||||
self.args = []
|
||||
if inlist == 0:
|
||||
inlist = []
|
||||
while len(inlist) != 0:
|
||||
added = 0
|
||||
for i, v in enumerate(self.args):
|
||||
if ((str(inlist[0][1]) == str(self.args[i][1])) and
|
||||
(str(inlist[0][2]) == str(self.args[i][2]))):
|
||||
self.args[i] = (self.args[i][0] + inlist[0][0],
|
||||
inlist[0][1], inlist[0][2])
|
||||
inlist.remove(inlist[0])
|
||||
added = 1
|
||||
break
|
||||
if added != 1:
|
||||
self.args.append(inlist[0])
|
||||
inlist.remove(inlist[0])
|
||||
i = 0
|
||||
# This code is to remove empty parts from the list
|
||||
while i < len(self.args):
|
||||
if ((self.args[i][0] == 0) | (self.args[i][1] == 0) |
|
||||
(self.args[i][2] == 0)):
|
||||
self.args.remove(self.args[i])
|
||||
i -= 1
|
||||
i += 1
|
||||
|
||||
@property
|
||||
def func(self):
|
||||
"""Returns the class Dyadic. """
|
||||
return Dyadic
|
||||
|
||||
def __add__(self, other):
|
||||
"""The add operator for Dyadic. """
|
||||
other = _check_dyadic(other)
|
||||
return Dyadic(self.args + other.args)
|
||||
|
||||
__radd__ = __add__
|
||||
|
||||
def __mul__(self, other):
|
||||
"""Multiplies the Dyadic by a sympifyable expression.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other : Sympafiable
|
||||
The scalar to multiply this Dyadic with
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, outer
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> d = outer(N.x, N.x)
|
||||
>>> 5 * d
|
||||
5*(N.x|N.x)
|
||||
|
||||
"""
|
||||
newlist = list(self.args)
|
||||
other = sympify(other)
|
||||
for i in range(len(newlist)):
|
||||
newlist[i] = (other * newlist[i][0], newlist[i][1],
|
||||
newlist[i][2])
|
||||
return Dyadic(newlist)
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def dot(self, other):
|
||||
"""The inner product operator for a Dyadic and a Dyadic or Vector.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other : Dyadic or Vector
|
||||
The other Dyadic or Vector to take the inner product with
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, outer
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> D1 = outer(N.x, N.y)
|
||||
>>> D2 = outer(N.y, N.y)
|
||||
>>> D1.dot(D2)
|
||||
(N.x|N.y)
|
||||
>>> D1.dot(N.y)
|
||||
N.x
|
||||
|
||||
"""
|
||||
from sympy.physics.vector.vector import Vector, _check_vector
|
||||
if isinstance(other, Dyadic):
|
||||
other = _check_dyadic(other)
|
||||
ol = Dyadic(0)
|
||||
for v in self.args:
|
||||
for v2 in other.args:
|
||||
ol += v[0] * v2[0] * (v[2].dot(v2[1])) * (v[1].outer(v2[2]))
|
||||
else:
|
||||
other = _check_vector(other)
|
||||
ol = Vector(0)
|
||||
for v in self.args:
|
||||
ol += v[0] * v[1] * (v[2].dot(other))
|
||||
return ol
|
||||
|
||||
# NOTE : supports non-advertised Dyadic & Dyadic, Dyadic & Vector notation
|
||||
__and__ = dot
|
||||
|
||||
def __truediv__(self, other):
|
||||
"""Divides the Dyadic by a sympifyable expression. """
|
||||
return self.__mul__(1 / other)
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Tests for equality.
|
||||
|
||||
Is currently weak; needs stronger comparison testing
|
||||
|
||||
"""
|
||||
|
||||
if other == 0:
|
||||
other = Dyadic(0)
|
||||
other = _check_dyadic(other)
|
||||
if (self.args == []) and (other.args == []):
|
||||
return True
|
||||
elif (self.args == []) or (other.args == []):
|
||||
return False
|
||||
return set(self.args) == set(other.args)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __neg__(self):
|
||||
return self * -1
|
||||
|
||||
def _latex(self, printer):
|
||||
ar = self.args # just to shorten things
|
||||
if len(ar) == 0:
|
||||
return str(0)
|
||||
ol = [] # output list, to be concatenated to a string
|
||||
for v in ar:
|
||||
# if the coef of the dyadic is 1, we skip the 1
|
||||
if v[0] == 1:
|
||||
ol.append(' + ' + printer._print(v[1]) + r"\otimes " +
|
||||
printer._print(v[2]))
|
||||
# if the coef of the dyadic is -1, we skip the 1
|
||||
elif v[0] == -1:
|
||||
ol.append(' - ' +
|
||||
printer._print(v[1]) +
|
||||
r"\otimes " +
|
||||
printer._print(v[2]))
|
||||
# If the coefficient of the dyadic is not 1 or -1,
|
||||
# we might wrap it in parentheses, for readability.
|
||||
elif v[0] != 0:
|
||||
arg_str = printer._print(v[0])
|
||||
if isinstance(v[0], Add):
|
||||
arg_str = '(%s)' % arg_str
|
||||
if arg_str.startswith('-'):
|
||||
arg_str = arg_str[1:]
|
||||
str_start = ' - '
|
||||
else:
|
||||
str_start = ' + '
|
||||
ol.append(str_start + arg_str + printer._print(v[1]) +
|
||||
r"\otimes " + printer._print(v[2]))
|
||||
outstr = ''.join(ol)
|
||||
if outstr.startswith(' + '):
|
||||
outstr = outstr[3:]
|
||||
elif outstr.startswith(' '):
|
||||
outstr = outstr[1:]
|
||||
return outstr
|
||||
|
||||
def _pretty(self, printer):
|
||||
e = self
|
||||
|
||||
class Fake:
|
||||
baseline = 0
|
||||
|
||||
def render(self, *args, **kwargs):
|
||||
ar = e.args # just to shorten things
|
||||
mpp = printer
|
||||
if len(ar) == 0:
|
||||
return str(0)
|
||||
bar = "\N{CIRCLED TIMES}" if printer._use_unicode else "|"
|
||||
ol = [] # output list, to be concatenated to a string
|
||||
for v in ar:
|
||||
# if the coef of the dyadic is 1, we skip the 1
|
||||
if v[0] == 1:
|
||||
ol.extend([" + ",
|
||||
mpp.doprint(v[1]),
|
||||
bar,
|
||||
mpp.doprint(v[2])])
|
||||
|
||||
# if the coef of the dyadic is -1, we skip the 1
|
||||
elif v[0] == -1:
|
||||
ol.extend([" - ",
|
||||
mpp.doprint(v[1]),
|
||||
bar,
|
||||
mpp.doprint(v[2])])
|
||||
|
||||
# If the coefficient of the dyadic is not 1 or -1,
|
||||
# we might wrap it in parentheses, for readability.
|
||||
elif v[0] != 0:
|
||||
if isinstance(v[0], Add):
|
||||
arg_str = mpp._print(
|
||||
v[0]).parens()[0]
|
||||
else:
|
||||
arg_str = mpp.doprint(v[0])
|
||||
if arg_str.startswith("-"):
|
||||
arg_str = arg_str[1:]
|
||||
str_start = " - "
|
||||
else:
|
||||
str_start = " + "
|
||||
ol.extend([str_start, arg_str, " ",
|
||||
mpp.doprint(v[1]),
|
||||
bar,
|
||||
mpp.doprint(v[2])])
|
||||
|
||||
outstr = "".join(ol)
|
||||
if outstr.startswith(" + "):
|
||||
outstr = outstr[3:]
|
||||
elif outstr.startswith(" "):
|
||||
outstr = outstr[1:]
|
||||
return outstr
|
||||
return Fake()
|
||||
|
||||
def __rsub__(self, other):
|
||||
return (-1 * self) + other
|
||||
|
||||
def _sympystr(self, printer):
|
||||
"""Printing method. """
|
||||
ar = self.args # just to shorten things
|
||||
if len(ar) == 0:
|
||||
return printer._print(0)
|
||||
ol = [] # output list, to be concatenated to a string
|
||||
for v in ar:
|
||||
# if the coef of the dyadic is 1, we skip the 1
|
||||
if v[0] == 1:
|
||||
ol.append(' + (' + printer._print(v[1]) + '|' +
|
||||
printer._print(v[2]) + ')')
|
||||
# if the coef of the dyadic is -1, we skip the 1
|
||||
elif v[0] == -1:
|
||||
ol.append(' - (' + printer._print(v[1]) + '|' +
|
||||
printer._print(v[2]) + ')')
|
||||
# If the coefficient of the dyadic is not 1 or -1,
|
||||
# we might wrap it in parentheses, for readability.
|
||||
elif v[0] != 0:
|
||||
arg_str = printer._print(v[0])
|
||||
if isinstance(v[0], Add):
|
||||
arg_str = "(%s)" % arg_str
|
||||
if arg_str[0] == '-':
|
||||
arg_str = arg_str[1:]
|
||||
str_start = ' - '
|
||||
else:
|
||||
str_start = ' + '
|
||||
ol.append(str_start + arg_str + '*(' +
|
||||
printer._print(v[1]) +
|
||||
'|' + printer._print(v[2]) + ')')
|
||||
outstr = ''.join(ol)
|
||||
if outstr.startswith(' + '):
|
||||
outstr = outstr[3:]
|
||||
elif outstr.startswith(' '):
|
||||
outstr = outstr[1:]
|
||||
return outstr
|
||||
|
||||
def __sub__(self, other):
|
||||
"""The subtraction operator. """
|
||||
return self.__add__(other * -1)
|
||||
|
||||
def cross(self, other):
|
||||
"""Returns the dyadic resulting from the dyadic vector cross product:
|
||||
Dyadic x Vector.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
other : Vector
|
||||
Vector to cross with.
|
||||
|
||||
Examples
|
||||
========
|
||||
>>> from sympy.physics.vector import ReferenceFrame, outer, cross
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> d = outer(N.x, N.x)
|
||||
>>> cross(d, N.y)
|
||||
(N.x|N.z)
|
||||
|
||||
"""
|
||||
from sympy.physics.vector.vector import _check_vector
|
||||
other = _check_vector(other)
|
||||
ol = Dyadic(0)
|
||||
for v in self.args:
|
||||
ol += v[0] * (v[1].outer((v[2].cross(other))))
|
||||
return ol
|
||||
|
||||
# NOTE : supports non-advertised Dyadic ^ Vector notation
|
||||
__xor__ = cross
|
||||
|
||||
def express(self, frame1, frame2=None):
|
||||
"""Expresses this Dyadic in alternate frame(s)
|
||||
|
||||
The first frame is the list side expression, the second frame is the
|
||||
right side; if Dyadic is in form A.x|B.y, you can express it in two
|
||||
different frames. If no second frame is given, the Dyadic is
|
||||
expressed in only one frame.
|
||||
|
||||
Calls the global express function
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame1 : ReferenceFrame
|
||||
The frame to express the left side of the Dyadic in
|
||||
frame2 : ReferenceFrame
|
||||
If provided, the frame to express the right side of the Dyadic in
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, outer, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> q = dynamicsymbols('q')
|
||||
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
||||
>>> d = outer(N.x, N.x)
|
||||
>>> d.express(B, N)
|
||||
cos(q)*(B.x|N.x) - sin(q)*(B.y|N.x)
|
||||
|
||||
"""
|
||||
from sympy.physics.vector.functions import express
|
||||
return express(self, frame1, frame2)
|
||||
|
||||
def to_matrix(self, reference_frame, second_reference_frame=None):
|
||||
"""Returns the matrix form of the dyadic with respect to one or two
|
||||
reference frames.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
reference_frame : ReferenceFrame
|
||||
The reference frame that the rows and columns of the matrix
|
||||
correspond to. If a second reference frame is provided, this
|
||||
only corresponds to the rows of the matrix.
|
||||
second_reference_frame : ReferenceFrame, optional, default=None
|
||||
The reference frame that the columns of the matrix correspond
|
||||
to.
|
||||
|
||||
Returns
|
||||
-------
|
||||
matrix : ImmutableMatrix, shape(3,3)
|
||||
The matrix that gives the 2D tensor form.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, trigsimp
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy.physics.mechanics import inertia
|
||||
>>> Ixx, Iyy, Izz, Ixy, Iyz, Ixz = symbols('Ixx, Iyy, Izz, Ixy, Iyz, Ixz')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> inertia_dyadic = inertia(N, Ixx, Iyy, Izz, Ixy, Iyz, Ixz)
|
||||
>>> inertia_dyadic.to_matrix(N)
|
||||
Matrix([
|
||||
[Ixx, Ixy, Ixz],
|
||||
[Ixy, Iyy, Iyz],
|
||||
[Ixz, Iyz, Izz]])
|
||||
>>> beta = symbols('beta')
|
||||
>>> A = N.orientnew('A', 'Axis', (beta, N.x))
|
||||
>>> trigsimp(inertia_dyadic.to_matrix(A))
|
||||
Matrix([
|
||||
[ Ixx, Ixy*cos(beta) + Ixz*sin(beta), -Ixy*sin(beta) + Ixz*cos(beta)],
|
||||
[ Ixy*cos(beta) + Ixz*sin(beta), Iyy*cos(2*beta)/2 + Iyy/2 + Iyz*sin(2*beta) - Izz*cos(2*beta)/2 + Izz/2, -Iyy*sin(2*beta)/2 + Iyz*cos(2*beta) + Izz*sin(2*beta)/2],
|
||||
[-Ixy*sin(beta) + Ixz*cos(beta), -Iyy*sin(2*beta)/2 + Iyz*cos(2*beta) + Izz*sin(2*beta)/2, -Iyy*cos(2*beta)/2 + Iyy/2 - Iyz*sin(2*beta) + Izz*cos(2*beta)/2 + Izz/2]])
|
||||
|
||||
"""
|
||||
|
||||
if second_reference_frame is None:
|
||||
second_reference_frame = reference_frame
|
||||
|
||||
return Matrix([i.dot(self).dot(j) for i in reference_frame for j in
|
||||
second_reference_frame]).reshape(3, 3)
|
||||
|
||||
def doit(self, **hints):
|
||||
"""Calls .doit() on each term in the Dyadic"""
|
||||
return sum([Dyadic([(v[0].doit(**hints), v[1], v[2])])
|
||||
for v in self.args], Dyadic(0))
|
||||
|
||||
def dt(self, frame):
|
||||
"""Take the time derivative of this Dyadic in a frame.
|
||||
|
||||
This function calls the global time_derivative method
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame to take the time derivative in
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, outer, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> q = dynamicsymbols('q')
|
||||
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
||||
>>> d = outer(N.x, N.x)
|
||||
>>> d.dt(B)
|
||||
- q'*(N.y|N.x) - q'*(N.x|N.y)
|
||||
|
||||
"""
|
||||
from sympy.physics.vector.functions import time_derivative
|
||||
return time_derivative(self, frame)
|
||||
|
||||
def simplify(self):
|
||||
"""Returns a simplified Dyadic."""
|
||||
out = Dyadic(0)
|
||||
for v in self.args:
|
||||
out += Dyadic([(v[0].simplify(), v[1], v[2])])
|
||||
return out
|
||||
|
||||
def subs(self, *args, **kwargs):
|
||||
"""Substitution on the Dyadic.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy import Symbol
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> s = Symbol('s')
|
||||
>>> a = s*(N.x|N.x)
|
||||
>>> a.subs({s: 2})
|
||||
2*(N.x|N.x)
|
||||
|
||||
"""
|
||||
|
||||
return sum([Dyadic([(v[0].subs(*args, **kwargs), v[1], v[2])])
|
||||
for v in self.args], Dyadic(0))
|
||||
|
||||
def applyfunc(self, f):
|
||||
"""Apply a function to each component of a Dyadic."""
|
||||
if not callable(f):
|
||||
raise TypeError("`f` must be callable.")
|
||||
|
||||
out = Dyadic(0)
|
||||
for a, b, c in self.args:
|
||||
out += f(a) * (b.outer(c))
|
||||
return out
|
||||
|
||||
def _eval_evalf(self, prec):
|
||||
if not self.args:
|
||||
return self
|
||||
new_args = []
|
||||
dps = prec_to_dps(prec)
|
||||
for inlist in self.args:
|
||||
new_inlist = list(inlist)
|
||||
new_inlist[0] = inlist[0].evalf(n=dps)
|
||||
new_args.append(tuple(new_inlist))
|
||||
return Dyadic(new_args)
|
||||
|
||||
def xreplace(self, rule):
|
||||
"""
|
||||
Replace occurrences of objects within the measure numbers of the
|
||||
Dyadic.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
rule : dict-like
|
||||
Expresses a replacement rule.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Dyadic
|
||||
Result of the replacement.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, pi
|
||||
>>> from sympy.physics.vector import ReferenceFrame, outer
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> D = outer(N.x, N.x)
|
||||
>>> x, y, z = symbols('x y z')
|
||||
>>> ((1 + x*y) * D).xreplace({x: pi})
|
||||
(pi*y + 1)*(N.x|N.x)
|
||||
>>> ((1 + x*y) * D).xreplace({x: pi, y: 2})
|
||||
(1 + 2*pi)*(N.x|N.x)
|
||||
|
||||
Replacements occur only if an entire node in the expression tree is
|
||||
matched:
|
||||
|
||||
>>> ((x*y + z) * D).xreplace({x*y: pi})
|
||||
(z + pi)*(N.x|N.x)
|
||||
>>> ((x*y*z) * D).xreplace({x*y: pi})
|
||||
x*y*z*(N.x|N.x)
|
||||
|
||||
"""
|
||||
|
||||
new_args = []
|
||||
for inlist in self.args:
|
||||
new_inlist = list(inlist)
|
||||
new_inlist[0] = new_inlist[0].xreplace(rule)
|
||||
new_args.append(tuple(new_inlist))
|
||||
return Dyadic(new_args)
|
||||
|
||||
|
||||
def _check_dyadic(other):
|
||||
if not isinstance(other, Dyadic):
|
||||
raise TypeError('A Dyadic must be supplied')
|
||||
return other
|
||||
@@ -0,0 +1,313 @@
|
||||
from sympy.core.function import diff
|
||||
from sympy.core.singleton import S
|
||||
from sympy.integrals.integrals import integrate
|
||||
from sympy.physics.vector import Vector, express
|
||||
from sympy.physics.vector.frame import _check_frame
|
||||
from sympy.physics.vector.vector import _check_vector
|
||||
|
||||
|
||||
__all__ = ['curl', 'divergence', 'gradient', 'is_conservative',
|
||||
'is_solenoidal', 'scalar_potential',
|
||||
'scalar_potential_difference']
|
||||
|
||||
|
||||
def curl(vect, frame):
|
||||
"""
|
||||
Returns the curl of a vector field computed wrt the coordinate
|
||||
symbols of the given frame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
vect : Vector
|
||||
The vector operand
|
||||
|
||||
frame : ReferenceFrame
|
||||
The reference frame to calculate the curl in
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy.physics.vector import curl
|
||||
>>> R = ReferenceFrame('R')
|
||||
>>> v1 = R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z
|
||||
>>> curl(v1, R)
|
||||
0
|
||||
>>> v2 = R[0]*R[1]*R[2]*R.x
|
||||
>>> curl(v2, R)
|
||||
R_x*R_y*R.y - R_x*R_z*R.z
|
||||
|
||||
"""
|
||||
|
||||
_check_vector(vect)
|
||||
if vect == 0:
|
||||
return Vector(0)
|
||||
vect = express(vect, frame, variables=True)
|
||||
# A mechanical approach to avoid looping overheads
|
||||
vectx = vect.dot(frame.x)
|
||||
vecty = vect.dot(frame.y)
|
||||
vectz = vect.dot(frame.z)
|
||||
outvec = Vector(0)
|
||||
outvec += (diff(vectz, frame[1]) - diff(vecty, frame[2])) * frame.x
|
||||
outvec += (diff(vectx, frame[2]) - diff(vectz, frame[0])) * frame.y
|
||||
outvec += (diff(vecty, frame[0]) - diff(vectx, frame[1])) * frame.z
|
||||
return outvec
|
||||
|
||||
|
||||
def divergence(vect, frame):
|
||||
"""
|
||||
Returns the divergence of a vector field computed wrt the coordinate
|
||||
symbols of the given frame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
vect : Vector
|
||||
The vector operand
|
||||
|
||||
frame : ReferenceFrame
|
||||
The reference frame to calculate the divergence in
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy.physics.vector import divergence
|
||||
>>> R = ReferenceFrame('R')
|
||||
>>> v1 = R[0]*R[1]*R[2] * (R.x+R.y+R.z)
|
||||
>>> divergence(v1, R)
|
||||
R_x*R_y + R_x*R_z + R_y*R_z
|
||||
>>> v2 = 2*R[1]*R[2]*R.y
|
||||
>>> divergence(v2, R)
|
||||
2*R_z
|
||||
|
||||
"""
|
||||
|
||||
_check_vector(vect)
|
||||
if vect == 0:
|
||||
return S.Zero
|
||||
vect = express(vect, frame, variables=True)
|
||||
vectx = vect.dot(frame.x)
|
||||
vecty = vect.dot(frame.y)
|
||||
vectz = vect.dot(frame.z)
|
||||
out = S.Zero
|
||||
out += diff(vectx, frame[0])
|
||||
out += diff(vecty, frame[1])
|
||||
out += diff(vectz, frame[2])
|
||||
return out
|
||||
|
||||
|
||||
def gradient(scalar, frame):
|
||||
"""
|
||||
Returns the vector gradient of a scalar field computed wrt the
|
||||
coordinate symbols of the given frame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
scalar : sympifiable
|
||||
The scalar field to take the gradient of
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame to calculate the gradient in
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy.physics.vector import gradient
|
||||
>>> R = ReferenceFrame('R')
|
||||
>>> s1 = R[0]*R[1]*R[2]
|
||||
>>> gradient(s1, R)
|
||||
R_y*R_z*R.x + R_x*R_z*R.y + R_x*R_y*R.z
|
||||
>>> s2 = 5*R[0]**2*R[2]
|
||||
>>> gradient(s2, R)
|
||||
10*R_x*R_z*R.x + 5*R_x**2*R.z
|
||||
|
||||
"""
|
||||
|
||||
_check_frame(frame)
|
||||
outvec = Vector(0)
|
||||
scalar = express(scalar, frame, variables=True)
|
||||
for i, x in enumerate(frame):
|
||||
outvec += diff(scalar, frame[i]) * x # noqa: PLR1736
|
||||
return outvec
|
||||
|
||||
|
||||
def is_conservative(field):
|
||||
"""
|
||||
Checks if a field is conservative.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
field : Vector
|
||||
The field to check for conservative property
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy.physics.vector import is_conservative
|
||||
>>> R = ReferenceFrame('R')
|
||||
>>> is_conservative(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z)
|
||||
True
|
||||
>>> is_conservative(R[2] * R.y)
|
||||
False
|
||||
|
||||
"""
|
||||
|
||||
# Field is conservative irrespective of frame
|
||||
# Take the first frame in the result of the separate method of Vector
|
||||
if field == Vector(0):
|
||||
return True
|
||||
frame = list(field.separate())[0]
|
||||
return curl(field, frame).simplify() == Vector(0)
|
||||
|
||||
|
||||
def is_solenoidal(field):
|
||||
"""
|
||||
Checks if a field is solenoidal.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
field : Vector
|
||||
The field to check for solenoidal property
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy.physics.vector import is_solenoidal
|
||||
>>> R = ReferenceFrame('R')
|
||||
>>> is_solenoidal(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z)
|
||||
True
|
||||
>>> is_solenoidal(R[1] * R.y)
|
||||
False
|
||||
|
||||
"""
|
||||
|
||||
# Field is solenoidal irrespective of frame
|
||||
# Take the first frame in the result of the separate method in Vector
|
||||
if field == Vector(0):
|
||||
return True
|
||||
frame = list(field.separate())[0]
|
||||
return divergence(field, frame).simplify() is S.Zero
|
||||
|
||||
|
||||
def scalar_potential(field, frame):
|
||||
"""
|
||||
Returns the scalar potential function of a field in a given frame
|
||||
(without the added integration constant).
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
field : Vector
|
||||
The vector field whose scalar potential function is to be
|
||||
calculated
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame to do the calculation in
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy.physics.vector import scalar_potential, gradient
|
||||
>>> R = ReferenceFrame('R')
|
||||
>>> scalar_potential(R.z, R) == R[2]
|
||||
True
|
||||
>>> scalar_field = 2*R[0]**2*R[1]*R[2]
|
||||
>>> grad_field = gradient(scalar_field, R)
|
||||
>>> scalar_potential(grad_field, R)
|
||||
2*R_x**2*R_y*R_z
|
||||
|
||||
"""
|
||||
|
||||
# Check whether field is conservative
|
||||
if not is_conservative(field):
|
||||
raise ValueError("Field is not conservative")
|
||||
if field == Vector(0):
|
||||
return S.Zero
|
||||
# Express the field exntirely in frame
|
||||
# Substitute coordinate variables also
|
||||
_check_frame(frame)
|
||||
field = express(field, frame, variables=True)
|
||||
# Make a list of dimensions of the frame
|
||||
dimensions = list(frame)
|
||||
# Calculate scalar potential function
|
||||
temp_function = integrate(field.dot(dimensions[0]), frame[0])
|
||||
for i, dim in enumerate(dimensions[1:]):
|
||||
partial_diff = diff(temp_function, frame[i + 1])
|
||||
partial_diff = field.dot(dim) - partial_diff
|
||||
temp_function += integrate(partial_diff, frame[i + 1])
|
||||
return temp_function
|
||||
|
||||
|
||||
def scalar_potential_difference(field, frame, point1, point2, origin):
|
||||
"""
|
||||
Returns the scalar potential difference between two points in a
|
||||
certain frame, wrt a given field.
|
||||
|
||||
If a scalar field is provided, its values at the two points are
|
||||
considered. If a conservative vector field is provided, the values
|
||||
of its scalar potential function at the two points are used.
|
||||
|
||||
Returns (potential at position 2) - (potential at position 1)
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
field : Vector/sympyfiable
|
||||
The field to calculate wrt
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame to do the calculations in
|
||||
|
||||
point1 : Point
|
||||
The initial Point in given frame
|
||||
|
||||
position2 : Point
|
||||
The second Point in the given frame
|
||||
|
||||
origin : Point
|
||||
The Point to use as reference point for position vector
|
||||
calculation
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, Point
|
||||
>>> from sympy.physics.vector import scalar_potential_difference
|
||||
>>> R = ReferenceFrame('R')
|
||||
>>> O = Point('O')
|
||||
>>> P = O.locatenew('P', R[0]*R.x + R[1]*R.y + R[2]*R.z)
|
||||
>>> vectfield = 4*R[0]*R[1]*R.x + 2*R[0]**2*R.y
|
||||
>>> scalar_potential_difference(vectfield, R, O, P, O)
|
||||
2*R_x**2*R_y
|
||||
>>> Q = O.locatenew('O', 3*R.x + R.y + 2*R.z)
|
||||
>>> scalar_potential_difference(vectfield, R, P, Q, O)
|
||||
-2*R_x**2*R_y + 18
|
||||
|
||||
"""
|
||||
|
||||
_check_frame(frame)
|
||||
if isinstance(field, Vector):
|
||||
# Get the scalar potential function
|
||||
scalar_fn = scalar_potential(field, frame)
|
||||
else:
|
||||
# Field is a scalar
|
||||
scalar_fn = field
|
||||
# Express positions in required frame
|
||||
position1 = express(point1.pos_from(origin), frame, variables=True)
|
||||
position2 = express(point2.pos_from(origin), frame, variables=True)
|
||||
# Get the two positions as substitution dicts for coordinate variables
|
||||
subs_dict1 = {}
|
||||
subs_dict2 = {}
|
||||
for i, x in enumerate(frame):
|
||||
subs_dict1[frame[i]] = x.dot(position1)
|
||||
subs_dict2[frame[i]] = x.dot(position2)
|
||||
return scalar_fn.subs(subs_dict2) - scalar_fn.subs(subs_dict1)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,650 @@
|
||||
from functools import reduce
|
||||
|
||||
from sympy import (sympify, diff, sin, cos, Matrix, symbols,
|
||||
Function, S, Symbol, linear_eq_to_matrix)
|
||||
from sympy.integrals.integrals import integrate
|
||||
from sympy.simplify.trigsimp import trigsimp
|
||||
from .vector import Vector, _check_vector
|
||||
from .frame import CoordinateSym, _check_frame
|
||||
from .dyadic import Dyadic
|
||||
from .printing import vprint, vsprint, vpprint, vlatex, init_vprinting
|
||||
from sympy.utilities.iterables import iterable
|
||||
from sympy.utilities.misc import translate
|
||||
|
||||
__all__ = ['cross', 'dot', 'express', 'time_derivative', 'outer',
|
||||
'kinematic_equations', 'get_motion_params', 'partial_velocity',
|
||||
'dynamicsymbols', 'vprint', 'vsprint', 'vpprint', 'vlatex',
|
||||
'init_vprinting']
|
||||
|
||||
|
||||
def cross(vec1, vec2):
|
||||
"""Cross product convenience wrapper for Vector.cross(): \n"""
|
||||
if not isinstance(vec1, (Vector, Dyadic)):
|
||||
raise TypeError('Cross product is between two vectors')
|
||||
return vec1 ^ vec2
|
||||
|
||||
|
||||
cross.__doc__ += Vector.cross.__doc__ # type: ignore
|
||||
|
||||
|
||||
def dot(vec1, vec2):
|
||||
"""Dot product convenience wrapper for Vector.dot(): \n"""
|
||||
if not isinstance(vec1, (Vector, Dyadic)):
|
||||
raise TypeError('Dot product is between two vectors')
|
||||
return vec1 & vec2
|
||||
|
||||
|
||||
dot.__doc__ += Vector.dot.__doc__ # type: ignore
|
||||
|
||||
|
||||
def express(expr, frame, frame2=None, variables=False):
|
||||
"""
|
||||
Global function for 'express' functionality.
|
||||
|
||||
Re-expresses a Vector, scalar(sympyfiable) or Dyadic in given frame.
|
||||
|
||||
Refer to the local methods of Vector and Dyadic for details.
|
||||
If 'variables' is True, then the coordinate variables (CoordinateSym
|
||||
instances) of other frames present in the vector/scalar field or
|
||||
dyadic expression are also substituted in terms of the base scalars of
|
||||
this frame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : Vector/Dyadic/scalar(sympyfiable)
|
||||
The expression to re-express in ReferenceFrame 'frame'
|
||||
|
||||
frame: ReferenceFrame
|
||||
The reference frame to express expr in
|
||||
|
||||
frame2 : ReferenceFrame
|
||||
The other frame required for re-expression(only for Dyadic expr)
|
||||
|
||||
variables : boolean
|
||||
Specifies whether to substitute the coordinate variables present
|
||||
in expr, in terms of those of frame
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, outer, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> q = dynamicsymbols('q')
|
||||
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
||||
>>> d = outer(N.x, N.x)
|
||||
>>> from sympy.physics.vector import express
|
||||
>>> express(d, B, N)
|
||||
cos(q)*(B.x|N.x) - sin(q)*(B.y|N.x)
|
||||
>>> express(B.x, N)
|
||||
cos(q)*N.x + sin(q)*N.y
|
||||
>>> express(N[0], B, variables=True)
|
||||
B_x*cos(q) - B_y*sin(q)
|
||||
|
||||
"""
|
||||
|
||||
_check_frame(frame)
|
||||
|
||||
if expr == 0:
|
||||
return expr
|
||||
|
||||
if isinstance(expr, Vector):
|
||||
# Given expr is a Vector
|
||||
if variables:
|
||||
# If variables attribute is True, substitute the coordinate
|
||||
# variables in the Vector
|
||||
frame_list = [x[-1] for x in expr.args]
|
||||
subs_dict = {}
|
||||
for f in frame_list:
|
||||
subs_dict.update(f.variable_map(frame))
|
||||
expr = expr.subs(subs_dict)
|
||||
# Re-express in this frame
|
||||
outvec = Vector([])
|
||||
for v in expr.args:
|
||||
if v[1] != frame:
|
||||
temp = frame.dcm(v[1]) * v[0]
|
||||
if Vector.simp:
|
||||
temp = temp.applyfunc(lambda x:
|
||||
trigsimp(x, method='fu'))
|
||||
outvec += Vector([(temp, frame)])
|
||||
else:
|
||||
outvec += Vector([v])
|
||||
return outvec
|
||||
|
||||
if isinstance(expr, Dyadic):
|
||||
if frame2 is None:
|
||||
frame2 = frame
|
||||
_check_frame(frame2)
|
||||
ol = Dyadic(0)
|
||||
for v in expr.args:
|
||||
ol += express(v[0], frame, variables=variables) * \
|
||||
(express(v[1], frame, variables=variables) |
|
||||
express(v[2], frame2, variables=variables))
|
||||
return ol
|
||||
|
||||
else:
|
||||
if variables:
|
||||
# Given expr is a scalar field
|
||||
frame_set = set()
|
||||
expr = sympify(expr)
|
||||
# Substitute all the coordinate variables
|
||||
for x in expr.free_symbols:
|
||||
if isinstance(x, CoordinateSym) and x.frame != frame:
|
||||
frame_set.add(x.frame)
|
||||
subs_dict = {}
|
||||
for f in frame_set:
|
||||
subs_dict.update(f.variable_map(frame))
|
||||
return expr.subs(subs_dict)
|
||||
return expr
|
||||
|
||||
|
||||
def time_derivative(expr, frame, order=1):
|
||||
"""
|
||||
Calculate the time derivative of a vector/scalar field function
|
||||
or dyadic expression in given frame.
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
https://en.wikipedia.org/wiki/Rotating_reference_frame#Time_derivatives_in_the_two_frames
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : Vector/Dyadic/sympifyable
|
||||
The expression whose time derivative is to be calculated
|
||||
|
||||
frame : ReferenceFrame
|
||||
The reference frame to calculate the time derivative in
|
||||
|
||||
order : integer
|
||||
The order of the derivative to be calculated
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> from sympy import Symbol
|
||||
>>> q1 = Symbol('q1')
|
||||
>>> u1 = dynamicsymbols('u1')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> A = N.orientnew('A', 'Axis', [q1, N.x])
|
||||
>>> v = u1 * N.x
|
||||
>>> A.set_ang_vel(N, 10*A.x)
|
||||
>>> from sympy.physics.vector import time_derivative
|
||||
>>> time_derivative(v, N)
|
||||
u1'*N.x
|
||||
>>> time_derivative(u1*A[0], N)
|
||||
N_x*u1'
|
||||
>>> B = N.orientnew('B', 'Axis', [u1, N.z])
|
||||
>>> from sympy.physics.vector import outer
|
||||
>>> d = outer(N.x, N.x)
|
||||
>>> time_derivative(d, B)
|
||||
- u1'*(N.y|N.x) - u1'*(N.x|N.y)
|
||||
|
||||
"""
|
||||
|
||||
t = dynamicsymbols._t
|
||||
_check_frame(frame)
|
||||
|
||||
if order == 0:
|
||||
return expr
|
||||
if order % 1 != 0 or order < 0:
|
||||
raise ValueError("Unsupported value of order entered")
|
||||
|
||||
if isinstance(expr, Vector):
|
||||
outlist = []
|
||||
for v in expr.args:
|
||||
if v[1] == frame:
|
||||
outlist += [(express(v[0], frame, variables=True).diff(t),
|
||||
frame)]
|
||||
else:
|
||||
outlist += (time_derivative(Vector([v]), v[1]) +
|
||||
(v[1].ang_vel_in(frame) ^ Vector([v]))).args
|
||||
outvec = Vector(outlist)
|
||||
return time_derivative(outvec, frame, order - 1)
|
||||
|
||||
if isinstance(expr, Dyadic):
|
||||
ol = Dyadic(0)
|
||||
for v in expr.args:
|
||||
ol += (v[0].diff(t) * (v[1] | v[2]))
|
||||
ol += (v[0] * (time_derivative(v[1], frame) | v[2]))
|
||||
ol += (v[0] * (v[1] | time_derivative(v[2], frame)))
|
||||
return time_derivative(ol, frame, order - 1)
|
||||
|
||||
else:
|
||||
return diff(express(expr, frame, variables=True), t, order)
|
||||
|
||||
|
||||
def outer(vec1, vec2):
|
||||
"""Outer product convenience wrapper for Vector.outer():\n"""
|
||||
if not isinstance(vec1, Vector):
|
||||
raise TypeError('Outer product is between two Vectors')
|
||||
return vec1.outer(vec2)
|
||||
|
||||
|
||||
outer.__doc__ += Vector.outer.__doc__ # type: ignore
|
||||
|
||||
|
||||
def kinematic_equations(speeds, coords, rot_type, rot_order=''):
|
||||
"""Gives equations relating the qdot's to u's for a rotation type.
|
||||
|
||||
Supply rotation type and order as in orient. Speeds are assumed to be
|
||||
body-fixed; if we are defining the orientation of B in A using by rot_type,
|
||||
the angular velocity of B in A is assumed to be in the form: speed[0]*B.x +
|
||||
speed[1]*B.y + speed[2]*B.z
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
speeds : list of length 3
|
||||
The body fixed angular velocity measure numbers.
|
||||
coords : list of length 3 or 4
|
||||
The coordinates used to define the orientation of the two frames.
|
||||
rot_type : str
|
||||
The type of rotation used to create the equations. Body, Space, or
|
||||
Quaternion only
|
||||
rot_order : str or int
|
||||
If applicable, the order of a series of rotations.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import dynamicsymbols
|
||||
>>> from sympy.physics.vector import kinematic_equations, vprint
|
||||
>>> u1, u2, u3 = dynamicsymbols('u1 u2 u3')
|
||||
>>> q1, q2, q3 = dynamicsymbols('q1 q2 q3')
|
||||
>>> vprint(kinematic_equations([u1,u2,u3], [q1,q2,q3], 'body', '313'),
|
||||
... order=None)
|
||||
[-(u1*sin(q3) + u2*cos(q3))/sin(q2) + q1', -u1*cos(q3) + u2*sin(q3) + q2', (u1*sin(q3) + u2*cos(q3))*cos(q2)/sin(q2) - u3 + q3']
|
||||
|
||||
"""
|
||||
|
||||
# Code below is checking and sanitizing input
|
||||
approved_orders = ('123', '231', '312', '132', '213', '321', '121', '131',
|
||||
'212', '232', '313', '323', '1', '2', '3', '')
|
||||
# make sure XYZ => 123 and rot_type is in lower case
|
||||
rot_order = translate(str(rot_order), 'XYZxyz', '123123')
|
||||
rot_type = rot_type.lower()
|
||||
|
||||
if not isinstance(speeds, (list, tuple)):
|
||||
raise TypeError('Need to supply speeds in a list')
|
||||
if len(speeds) != 3:
|
||||
raise TypeError('Need to supply 3 body-fixed speeds')
|
||||
if not isinstance(coords, (list, tuple)):
|
||||
raise TypeError('Need to supply coordinates in a list')
|
||||
if rot_type in ['body', 'space']:
|
||||
if rot_order not in approved_orders:
|
||||
raise ValueError('Not an acceptable rotation order')
|
||||
if len(coords) != 3:
|
||||
raise ValueError('Need 3 coordinates for body or space')
|
||||
# Actual hard-coded kinematic differential equations
|
||||
w1, w2, w3 = speeds
|
||||
if w1 == w2 == w3 == 0:
|
||||
return [S.Zero]*3
|
||||
q1, q2, q3 = coords
|
||||
q1d, q2d, q3d = [diff(i, dynamicsymbols._t) for i in coords]
|
||||
s1, s2, s3 = [sin(q1), sin(q2), sin(q3)]
|
||||
c1, c2, c3 = [cos(q1), cos(q2), cos(q3)]
|
||||
if rot_type == 'body':
|
||||
if rot_order == '123':
|
||||
return [q1d - (w1 * c3 - w2 * s3) / c2, q2d - w1 * s3 - w2 *
|
||||
c3, q3d - (-w1 * c3 + w2 * s3) * s2 / c2 - w3]
|
||||
if rot_order == '231':
|
||||
return [q1d - (w2 * c3 - w3 * s3) / c2, q2d - w2 * s3 - w3 *
|
||||
c3, q3d - w1 - (- w2 * c3 + w3 * s3) * s2 / c2]
|
||||
if rot_order == '312':
|
||||
return [q1d - (-w1 * s3 + w3 * c3) / c2, q2d - w1 * c3 - w3 *
|
||||
s3, q3d - (w1 * s3 - w3 * c3) * s2 / c2 - w2]
|
||||
if rot_order == '132':
|
||||
return [q1d - (w1 * c3 + w3 * s3) / c2, q2d + w1 * s3 - w3 *
|
||||
c3, q3d - (w1 * c3 + w3 * s3) * s2 / c2 - w2]
|
||||
if rot_order == '213':
|
||||
return [q1d - (w1 * s3 + w2 * c3) / c2, q2d - w1 * c3 + w2 *
|
||||
s3, q3d - (w1 * s3 + w2 * c3) * s2 / c2 - w3]
|
||||
if rot_order == '321':
|
||||
return [q1d - (w2 * s3 + w3 * c3) / c2, q2d - w2 * c3 + w3 *
|
||||
s3, q3d - w1 - (w2 * s3 + w3 * c3) * s2 / c2]
|
||||
if rot_order == '121':
|
||||
return [q1d - (w2 * s3 + w3 * c3) / s2, q2d - w2 * c3 + w3 *
|
||||
s3, q3d - w1 + (w2 * s3 + w3 * c3) * c2 / s2]
|
||||
if rot_order == '131':
|
||||
return [q1d - (-w2 * c3 + w3 * s3) / s2, q2d - w2 * s3 - w3 *
|
||||
c3, q3d - w1 - (w2 * c3 - w3 * s3) * c2 / s2]
|
||||
if rot_order == '212':
|
||||
return [q1d - (w1 * s3 - w3 * c3) / s2, q2d - w1 * c3 - w3 *
|
||||
s3, q3d - (-w1 * s3 + w3 * c3) * c2 / s2 - w2]
|
||||
if rot_order == '232':
|
||||
return [q1d - (w1 * c3 + w3 * s3) / s2, q2d + w1 * s3 - w3 *
|
||||
c3, q3d + (w1 * c3 + w3 * s3) * c2 / s2 - w2]
|
||||
if rot_order == '313':
|
||||
return [q1d - (w1 * s3 + w2 * c3) / s2, q2d - w1 * c3 + w2 *
|
||||
s3, q3d + (w1 * s3 + w2 * c3) * c2 / s2 - w3]
|
||||
if rot_order == '323':
|
||||
return [q1d - (-w1 * c3 + w2 * s3) / s2, q2d - w1 * s3 - w2 *
|
||||
c3, q3d - (w1 * c3 - w2 * s3) * c2 / s2 - w3]
|
||||
if rot_type == 'space':
|
||||
if rot_order == '123':
|
||||
return [q1d - w1 - (w2 * s1 + w3 * c1) * s2 / c2, q2d - w2 *
|
||||
c1 + w3 * s1, q3d - (w2 * s1 + w3 * c1) / c2]
|
||||
if rot_order == '231':
|
||||
return [q1d - (w1 * c1 + w3 * s1) * s2 / c2 - w2, q2d + w1 *
|
||||
s1 - w3 * c1, q3d - (w1 * c1 + w3 * s1) / c2]
|
||||
if rot_order == '312':
|
||||
return [q1d - (w1 * s1 + w2 * c1) * s2 / c2 - w3, q2d - w1 *
|
||||
c1 + w2 * s1, q3d - (w1 * s1 + w2 * c1) / c2]
|
||||
if rot_order == '132':
|
||||
return [q1d - w1 - (-w2 * c1 + w3 * s1) * s2 / c2, q2d - w2 *
|
||||
s1 - w3 * c1, q3d - (w2 * c1 - w3 * s1) / c2]
|
||||
if rot_order == '213':
|
||||
return [q1d - (w1 * s1 - w3 * c1) * s2 / c2 - w2, q2d - w1 *
|
||||
c1 - w3 * s1, q3d - (-w1 * s1 + w3 * c1) / c2]
|
||||
if rot_order == '321':
|
||||
return [q1d - (-w1 * c1 + w2 * s1) * s2 / c2 - w3, q2d - w1 *
|
||||
s1 - w2 * c1, q3d - (w1 * c1 - w2 * s1) / c2]
|
||||
if rot_order == '121':
|
||||
return [q1d - w1 + (w2 * s1 + w3 * c1) * c2 / s2, q2d - w2 *
|
||||
c1 + w3 * s1, q3d - (w2 * s1 + w3 * c1) / s2]
|
||||
if rot_order == '131':
|
||||
return [q1d - w1 - (w2 * c1 - w3 * s1) * c2 / s2, q2d - w2 *
|
||||
s1 - w3 * c1, q3d - (-w2 * c1 + w3 * s1) / s2]
|
||||
if rot_order == '212':
|
||||
return [q1d - (-w1 * s1 + w3 * c1) * c2 / s2 - w2, q2d - w1 *
|
||||
c1 - w3 * s1, q3d - (w1 * s1 - w3 * c1) / s2]
|
||||
if rot_order == '232':
|
||||
return [q1d + (w1 * c1 + w3 * s1) * c2 / s2 - w2, q2d + w1 *
|
||||
s1 - w3 * c1, q3d - (w1 * c1 + w3 * s1) / s2]
|
||||
if rot_order == '313':
|
||||
return [q1d + (w1 * s1 + w2 * c1) * c2 / s2 - w3, q2d - w1 *
|
||||
c1 + w2 * s1, q3d - (w1 * s1 + w2 * c1) / s2]
|
||||
if rot_order == '323':
|
||||
return [q1d - (w1 * c1 - w2 * s1) * c2 / s2 - w3, q2d - w1 *
|
||||
s1 - w2 * c1, q3d - (-w1 * c1 + w2 * s1) / s2]
|
||||
elif rot_type == 'quaternion':
|
||||
if rot_order != '':
|
||||
raise ValueError('Cannot have rotation order for quaternion')
|
||||
if len(coords) != 4:
|
||||
raise ValueError('Need 4 coordinates for quaternion')
|
||||
# Actual hard-coded kinematic differential equations
|
||||
e0, e1, e2, e3 = coords
|
||||
w = Matrix(speeds + [0])
|
||||
E = Matrix([[e0, -e3, e2, e1],
|
||||
[e3, e0, -e1, e2],
|
||||
[-e2, e1, e0, e3],
|
||||
[-e1, -e2, -e3, e0]])
|
||||
edots = Matrix([diff(i, dynamicsymbols._t) for i in [e1, e2, e3, e0]])
|
||||
return list(edots.T - 0.5 * w.T * E.T)
|
||||
else:
|
||||
raise ValueError('Not an approved rotation type for this function')
|
||||
|
||||
|
||||
def get_motion_params(frame, **kwargs):
|
||||
"""
|
||||
Returns the three motion parameters - (acceleration, velocity, and
|
||||
position) as vectorial functions of time in the given frame.
|
||||
|
||||
If a higher order differential function is provided, the lower order
|
||||
functions are used as boundary conditions. For example, given the
|
||||
acceleration, the velocity and position parameters are taken as
|
||||
boundary conditions.
|
||||
|
||||
The values of time at which the boundary conditions are specified
|
||||
are taken from timevalue1(for position boundary condition) and
|
||||
timevalue2(for velocity boundary condition).
|
||||
|
||||
If any of the boundary conditions are not provided, they are taken
|
||||
to be zero by default (zero vectors, in case of vectorial inputs). If
|
||||
the boundary conditions are also functions of time, they are converted
|
||||
to constants by substituting the time values in the dynamicsymbols._t
|
||||
time Symbol.
|
||||
|
||||
This function can also be used for calculating rotational motion
|
||||
parameters. Have a look at the Parameters and Examples for more clarity.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame to express the motion parameters in
|
||||
|
||||
acceleration : Vector
|
||||
Acceleration of the object/frame as a function of time
|
||||
|
||||
velocity : Vector
|
||||
Velocity as function of time or as boundary condition
|
||||
of velocity at time = timevalue1
|
||||
|
||||
position : Vector
|
||||
Velocity as function of time or as boundary condition
|
||||
of velocity at time = timevalue1
|
||||
|
||||
timevalue1 : sympyfiable
|
||||
Value of time for position boundary condition
|
||||
|
||||
timevalue2 : sympyfiable
|
||||
Value of time for velocity boundary condition
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, get_motion_params, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> from sympy import symbols
|
||||
>>> R = ReferenceFrame('R')
|
||||
>>> v1, v2, v3 = dynamicsymbols('v1 v2 v3')
|
||||
>>> v = v1*R.x + v2*R.y + v3*R.z
|
||||
>>> get_motion_params(R, position = v)
|
||||
(v1''*R.x + v2''*R.y + v3''*R.z, v1'*R.x + v2'*R.y + v3'*R.z, v1*R.x + v2*R.y + v3*R.z)
|
||||
>>> a, b, c = symbols('a b c')
|
||||
>>> v = a*R.x + b*R.y + c*R.z
|
||||
>>> get_motion_params(R, velocity = v)
|
||||
(0, a*R.x + b*R.y + c*R.z, a*t*R.x + b*t*R.y + c*t*R.z)
|
||||
>>> parameters = get_motion_params(R, acceleration = v)
|
||||
>>> parameters[1]
|
||||
a*t*R.x + b*t*R.y + c*t*R.z
|
||||
>>> parameters[2]
|
||||
a*t**2/2*R.x + b*t**2/2*R.y + c*t**2/2*R.z
|
||||
|
||||
"""
|
||||
|
||||
def _process_vector_differential(vectdiff, condition, variable, ordinate,
|
||||
frame):
|
||||
"""
|
||||
Helper function for get_motion methods. Finds derivative of vectdiff
|
||||
wrt variable, and its integral using the specified boundary condition
|
||||
at value of variable = ordinate.
|
||||
Returns a tuple of - (derivative, function and integral) wrt vectdiff
|
||||
|
||||
"""
|
||||
|
||||
# Make sure boundary condition is independent of 'variable'
|
||||
if condition != 0:
|
||||
condition = express(condition, frame, variables=True)
|
||||
# Special case of vectdiff == 0
|
||||
if vectdiff == Vector(0):
|
||||
return (0, 0, condition)
|
||||
# Express vectdiff completely in condition's frame to give vectdiff1
|
||||
vectdiff1 = express(vectdiff, frame)
|
||||
# Find derivative of vectdiff
|
||||
vectdiff2 = time_derivative(vectdiff, frame)
|
||||
# Integrate and use boundary condition
|
||||
vectdiff0 = Vector(0)
|
||||
lims = (variable, ordinate, variable)
|
||||
for dim in frame:
|
||||
function1 = vectdiff1.dot(dim)
|
||||
abscissa = dim.dot(condition).subs({variable: ordinate})
|
||||
# Indefinite integral of 'function1' wrt 'variable', using
|
||||
# the given initial condition (ordinate, abscissa).
|
||||
vectdiff0 += (integrate(function1, lims) + abscissa) * dim
|
||||
# Return tuple
|
||||
return (vectdiff2, vectdiff, vectdiff0)
|
||||
|
||||
_check_frame(frame)
|
||||
# Decide mode of operation based on user's input
|
||||
if 'acceleration' in kwargs:
|
||||
mode = 2
|
||||
elif 'velocity' in kwargs:
|
||||
mode = 1
|
||||
else:
|
||||
mode = 0
|
||||
# All the possible parameters in kwargs
|
||||
# Not all are required for every case
|
||||
# If not specified, set to default values(may or may not be used in
|
||||
# calculations)
|
||||
conditions = ['acceleration', 'velocity', 'position',
|
||||
'timevalue', 'timevalue1', 'timevalue2']
|
||||
for i, x in enumerate(conditions):
|
||||
if x not in kwargs:
|
||||
if i < 3:
|
||||
kwargs[x] = Vector(0)
|
||||
else:
|
||||
kwargs[x] = S.Zero
|
||||
elif i < 3:
|
||||
_check_vector(kwargs[x])
|
||||
else:
|
||||
kwargs[x] = sympify(kwargs[x])
|
||||
if mode == 2:
|
||||
vel = _process_vector_differential(kwargs['acceleration'],
|
||||
kwargs['velocity'],
|
||||
dynamicsymbols._t,
|
||||
kwargs['timevalue2'], frame)[2]
|
||||
pos = _process_vector_differential(vel, kwargs['position'],
|
||||
dynamicsymbols._t,
|
||||
kwargs['timevalue1'], frame)[2]
|
||||
return (kwargs['acceleration'], vel, pos)
|
||||
elif mode == 1:
|
||||
return _process_vector_differential(kwargs['velocity'],
|
||||
kwargs['position'],
|
||||
dynamicsymbols._t,
|
||||
kwargs['timevalue1'], frame)
|
||||
else:
|
||||
vel = time_derivative(kwargs['position'], frame)
|
||||
acc = time_derivative(vel, frame)
|
||||
return (acc, vel, kwargs['position'])
|
||||
|
||||
|
||||
def partial_velocity(vel_vecs, gen_speeds, frame):
|
||||
"""Returns a list of partial velocities with respect to the provided
|
||||
generalized speeds in the given reference frame for each of the supplied
|
||||
velocity vectors.
|
||||
|
||||
The output is a list of lists. The outer list has a number of elements
|
||||
equal to the number of supplied velocity vectors. The inner lists are, for
|
||||
each velocity vector, the partial derivatives of that velocity vector with
|
||||
respect to the generalized speeds supplied.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
vel_vecs : iterable
|
||||
An iterable of velocity vectors (angular or linear).
|
||||
gen_speeds : iterable
|
||||
An iterable of generalized speeds.
|
||||
frame : ReferenceFrame
|
||||
The reference frame that the partial derivatives are going to be taken
|
||||
in.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame
|
||||
>>> from sympy.physics.vector import dynamicsymbols
|
||||
>>> from sympy.physics.vector import partial_velocity
|
||||
>>> u = dynamicsymbols('u')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> P = Point('P')
|
||||
>>> P.set_vel(N, u * N.x)
|
||||
>>> vel_vecs = [P.vel(N)]
|
||||
>>> gen_speeds = [u]
|
||||
>>> partial_velocity(vel_vecs, gen_speeds, N)
|
||||
[[N.x]]
|
||||
|
||||
"""
|
||||
|
||||
if not iterable(vel_vecs):
|
||||
raise TypeError('Velocity vectors must be contained in an iterable.')
|
||||
|
||||
if not iterable(gen_speeds):
|
||||
raise TypeError('Generalized speeds must be contained in an iterable')
|
||||
|
||||
vec_partials = []
|
||||
gen_speeds = list(gen_speeds)
|
||||
for vel in vel_vecs:
|
||||
partials = [Vector(0) for _ in gen_speeds]
|
||||
for components, ref in vel.args:
|
||||
mat, _ = linear_eq_to_matrix(components, gen_speeds)
|
||||
for i in range(len(gen_speeds)):
|
||||
for dim, direction in enumerate(ref):
|
||||
if mat[dim, i] != 0:
|
||||
partials[i] += direction * mat[dim, i]
|
||||
|
||||
vec_partials.append(partials)
|
||||
|
||||
return vec_partials
|
||||
|
||||
|
||||
def dynamicsymbols(names, level=0, **assumptions):
|
||||
"""Uses symbols and Function for functions of time.
|
||||
|
||||
Creates a SymPy UndefinedFunction, which is then initialized as a function
|
||||
of a variable, the default being Symbol('t').
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
names : str
|
||||
Names of the dynamic symbols you want to create; works the same way as
|
||||
inputs to symbols
|
||||
level : int
|
||||
Level of differentiation of the returned function; d/dt once of t,
|
||||
twice of t, etc.
|
||||
assumptions :
|
||||
- real(bool) : This is used to set the dynamicsymbol as real,
|
||||
by default is False.
|
||||
- positive(bool) : This is used to set the dynamicsymbol as positive,
|
||||
by default is False.
|
||||
- commutative(bool) : This is used to set the commutative property of
|
||||
a dynamicsymbol, by default is True.
|
||||
- integer(bool) : This is used to set the dynamicsymbol as integer,
|
||||
by default is False.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import dynamicsymbols
|
||||
>>> from sympy import diff, Symbol
|
||||
>>> q1 = dynamicsymbols('q1')
|
||||
>>> q1
|
||||
q1(t)
|
||||
>>> q2 = dynamicsymbols('q2', real=True)
|
||||
>>> q2.is_real
|
||||
True
|
||||
>>> q3 = dynamicsymbols('q3', positive=True)
|
||||
>>> q3.is_positive
|
||||
True
|
||||
>>> q4, q5 = dynamicsymbols('q4,q5', commutative=False)
|
||||
>>> bool(q4*q5 != q5*q4)
|
||||
True
|
||||
>>> q6 = dynamicsymbols('q6', integer=True)
|
||||
>>> q6.is_integer
|
||||
True
|
||||
>>> diff(q1, Symbol('t'))
|
||||
Derivative(q1(t), t)
|
||||
|
||||
"""
|
||||
esses = symbols(names, cls=Function, **assumptions)
|
||||
t = dynamicsymbols._t
|
||||
if iterable(esses):
|
||||
esses = [reduce(diff, [t] * level, e(t)) for e in esses]
|
||||
return esses
|
||||
else:
|
||||
return reduce(diff, [t] * level, esses(t))
|
||||
|
||||
|
||||
dynamicsymbols._t = Symbol('t') # type: ignore
|
||||
dynamicsymbols._str = '\'' # type: ignore
|
||||
@@ -0,0 +1,635 @@
|
||||
from .vector import Vector, _check_vector
|
||||
from .frame import _check_frame
|
||||
from warnings import warn
|
||||
from sympy.utilities.misc import filldedent
|
||||
|
||||
__all__ = ['Point']
|
||||
|
||||
|
||||
class Point:
|
||||
"""This object represents a point in a dynamic system.
|
||||
|
||||
It stores the: position, velocity, and acceleration of a point.
|
||||
The position is a vector defined as the vector distance from a parent
|
||||
point to this point.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
name : string
|
||||
The display name of the Point
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> O = Point('O')
|
||||
>>> P = Point('P')
|
||||
>>> u1, u2, u3 = dynamicsymbols('u1 u2 u3')
|
||||
>>> O.set_vel(N, u1 * N.x + u2 * N.y + u3 * N.z)
|
||||
>>> O.acc(N)
|
||||
u1'*N.x + u2'*N.y + u3'*N.z
|
||||
|
||||
``symbols()`` can be used to create multiple Points in a single step, for
|
||||
example:
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> from sympy import symbols
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> u1, u2 = dynamicsymbols('u1 u2')
|
||||
>>> A, B = symbols('A B', cls=Point)
|
||||
>>> type(A)
|
||||
<class 'sympy.physics.vector.point.Point'>
|
||||
>>> A.set_vel(N, u1 * N.x + u2 * N.y)
|
||||
>>> B.set_vel(N, u2 * N.x + u1 * N.y)
|
||||
>>> A.acc(N) - B.acc(N)
|
||||
(u1' - u2')*N.x + (-u1' + u2')*N.y
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
"""Initialization of a Point object. """
|
||||
self.name = name
|
||||
self._pos_dict = {}
|
||||
self._vel_dict = {}
|
||||
self._acc_dict = {}
|
||||
self._pdlist = [self._pos_dict, self._vel_dict, self._acc_dict]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def _check_point(self, other):
|
||||
if not isinstance(other, Point):
|
||||
raise TypeError('A Point must be supplied')
|
||||
|
||||
def _pdict_list(self, other, num):
|
||||
"""Returns a list of points that gives the shortest path with respect
|
||||
to position, velocity, or acceleration from this point to the provided
|
||||
point.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
other : Point
|
||||
A point that may be related to this point by position, velocity, or
|
||||
acceleration.
|
||||
num : integer
|
||||
0 for searching the position tree, 1 for searching the velocity
|
||||
tree, and 2 for searching the acceleration tree.
|
||||
|
||||
Returns
|
||||
=======
|
||||
list of Points
|
||||
A sequence of points from self to other.
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
It is not clear if num = 1 or num = 2 actually works because the keys
|
||||
to ``_vel_dict`` and ``_acc_dict`` are :class:`ReferenceFrame` objects
|
||||
which do not have the ``_pdlist`` attribute.
|
||||
|
||||
"""
|
||||
outlist = [[self]]
|
||||
oldlist = [[]]
|
||||
while outlist != oldlist:
|
||||
oldlist = outlist.copy()
|
||||
for v in outlist:
|
||||
templist = v[-1]._pdlist[num].keys()
|
||||
for v2 in templist:
|
||||
if not v.__contains__(v2):
|
||||
littletemplist = v + [v2]
|
||||
if not outlist.__contains__(littletemplist):
|
||||
outlist.append(littletemplist)
|
||||
for v in oldlist:
|
||||
if v[-1] != other:
|
||||
outlist.remove(v)
|
||||
outlist.sort(key=len)
|
||||
if len(outlist) != 0:
|
||||
return outlist[0]
|
||||
raise ValueError('No Connecting Path found between ' + other.name +
|
||||
' and ' + self.name)
|
||||
|
||||
def a1pt_theory(self, otherpoint, outframe, interframe):
|
||||
"""Sets the acceleration of this point with the 1-point theory.
|
||||
|
||||
The 1-point theory for point acceleration looks like this:
|
||||
|
||||
^N a^P = ^B a^P + ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B
|
||||
x r^OP) + 2 ^N omega^B x ^B v^P
|
||||
|
||||
where O is a point fixed in B, P is a point moving in B, and B is
|
||||
rotating in frame N.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
otherpoint : Point
|
||||
The first point of the 1-point theory (O)
|
||||
outframe : ReferenceFrame
|
||||
The frame we want this point's acceleration defined in (N)
|
||||
fixedframe : ReferenceFrame
|
||||
The intermediate frame in this calculation (B)
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame
|
||||
>>> from sympy.physics.vector import dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> q = dynamicsymbols('q')
|
||||
>>> q2 = dynamicsymbols('q2')
|
||||
>>> qd = dynamicsymbols('q', 1)
|
||||
>>> q2d = dynamicsymbols('q2', 1)
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> B = ReferenceFrame('B')
|
||||
>>> B.set_ang_vel(N, 5 * B.y)
|
||||
>>> O = Point('O')
|
||||
>>> P = O.locatenew('P', q * B.x + q2 * B.y)
|
||||
>>> P.set_vel(B, qd * B.x + q2d * B.y)
|
||||
>>> O.set_vel(N, 0)
|
||||
>>> P.a1pt_theory(O, N, B)
|
||||
(-25*q + q'')*B.x + q2''*B.y - 10*q'*B.z
|
||||
|
||||
"""
|
||||
|
||||
_check_frame(outframe)
|
||||
_check_frame(interframe)
|
||||
self._check_point(otherpoint)
|
||||
dist = self.pos_from(otherpoint)
|
||||
v = self.vel(interframe)
|
||||
a1 = otherpoint.acc(outframe)
|
||||
a2 = self.acc(interframe)
|
||||
omega = interframe.ang_vel_in(outframe)
|
||||
alpha = interframe.ang_acc_in(outframe)
|
||||
self.set_acc(outframe, a2 + 2 * (omega.cross(v)) + a1 +
|
||||
(alpha.cross(dist)) + (omega.cross(omega.cross(dist))))
|
||||
return self.acc(outframe)
|
||||
|
||||
def a2pt_theory(self, otherpoint, outframe, fixedframe):
|
||||
"""Sets the acceleration of this point with the 2-point theory.
|
||||
|
||||
The 2-point theory for point acceleration looks like this:
|
||||
|
||||
^N a^P = ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B x r^OP)
|
||||
|
||||
where O and P are both points fixed in frame B, which is rotating in
|
||||
frame N.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
otherpoint : Point
|
||||
The first point of the 2-point theory (O)
|
||||
outframe : ReferenceFrame
|
||||
The frame we want this point's acceleration defined in (N)
|
||||
fixedframe : ReferenceFrame
|
||||
The frame in which both points are fixed (B)
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> q = dynamicsymbols('q')
|
||||
>>> qd = dynamicsymbols('q', 1)
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
||||
>>> O = Point('O')
|
||||
>>> P = O.locatenew('P', 10 * B.x)
|
||||
>>> O.set_vel(N, 5 * N.x)
|
||||
>>> P.a2pt_theory(O, N, B)
|
||||
- 10*q'**2*B.x + 10*q''*B.y
|
||||
|
||||
"""
|
||||
|
||||
_check_frame(outframe)
|
||||
_check_frame(fixedframe)
|
||||
self._check_point(otherpoint)
|
||||
dist = self.pos_from(otherpoint)
|
||||
a = otherpoint.acc(outframe)
|
||||
omega = fixedframe.ang_vel_in(outframe)
|
||||
alpha = fixedframe.ang_acc_in(outframe)
|
||||
self.set_acc(outframe, a + (alpha.cross(dist)) +
|
||||
(omega.cross(omega.cross(dist))))
|
||||
return self.acc(outframe)
|
||||
|
||||
def acc(self, frame):
|
||||
"""The acceleration Vector of this Point in a ReferenceFrame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame in which the returned acceleration vector will be defined
|
||||
in.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> p1 = Point('p1')
|
||||
>>> p1.set_acc(N, 10 * N.x)
|
||||
>>> p1.acc(N)
|
||||
10*N.x
|
||||
|
||||
"""
|
||||
|
||||
_check_frame(frame)
|
||||
if not (frame in self._acc_dict):
|
||||
if self.vel(frame) != 0:
|
||||
return (self._vel_dict[frame]).dt(frame)
|
||||
else:
|
||||
return Vector(0)
|
||||
return self._acc_dict[frame]
|
||||
|
||||
def locatenew(self, name, value):
|
||||
"""Creates a new point with a position defined from this point.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
name : str
|
||||
The name for the new point
|
||||
value : Vector
|
||||
The position of the new point relative to this point
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, Point
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> P1 = Point('P1')
|
||||
>>> P2 = P1.locatenew('P2', 10 * N.x)
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(name, str):
|
||||
raise TypeError('Must supply a valid name')
|
||||
if value == 0:
|
||||
value = Vector(0)
|
||||
value = _check_vector(value)
|
||||
p = Point(name)
|
||||
p.set_pos(self, value)
|
||||
self.set_pos(p, -value)
|
||||
return p
|
||||
|
||||
def pos_from(self, otherpoint):
|
||||
"""Returns a Vector distance between this Point and the other Point.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
otherpoint : Point
|
||||
The otherpoint we are locating this one relative to
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> p1 = Point('p1')
|
||||
>>> p2 = Point('p2')
|
||||
>>> p1.set_pos(p2, 10 * N.x)
|
||||
>>> p1.pos_from(p2)
|
||||
10*N.x
|
||||
|
||||
"""
|
||||
|
||||
outvec = Vector(0)
|
||||
plist = self._pdict_list(otherpoint, 0)
|
||||
for i in range(len(plist) - 1):
|
||||
outvec += plist[i]._pos_dict[plist[i + 1]]
|
||||
return outvec
|
||||
|
||||
def set_acc(self, frame, value):
|
||||
"""Used to set the acceleration of this Point in a ReferenceFrame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame in which this point's acceleration is defined
|
||||
value : Vector
|
||||
The vector value of this point's acceleration in the frame
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> p1 = Point('p1')
|
||||
>>> p1.set_acc(N, 10 * N.x)
|
||||
>>> p1.acc(N)
|
||||
10*N.x
|
||||
|
||||
"""
|
||||
|
||||
if value == 0:
|
||||
value = Vector(0)
|
||||
value = _check_vector(value)
|
||||
_check_frame(frame)
|
||||
self._acc_dict.update({frame: value})
|
||||
|
||||
def set_pos(self, otherpoint, value):
|
||||
"""Used to set the position of this point w.r.t. another point.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
otherpoint : Point
|
||||
The other point which this point's location is defined relative to
|
||||
value : Vector
|
||||
The vector which defines the location of this point
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> p1 = Point('p1')
|
||||
>>> p2 = Point('p2')
|
||||
>>> p1.set_pos(p2, 10 * N.x)
|
||||
>>> p1.pos_from(p2)
|
||||
10*N.x
|
||||
|
||||
"""
|
||||
|
||||
if value == 0:
|
||||
value = Vector(0)
|
||||
value = _check_vector(value)
|
||||
self._check_point(otherpoint)
|
||||
self._pos_dict.update({otherpoint: value})
|
||||
otherpoint._pos_dict.update({self: -value})
|
||||
|
||||
def set_vel(self, frame, value):
|
||||
"""Sets the velocity Vector of this Point in a ReferenceFrame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame in which this point's velocity is defined
|
||||
value : Vector
|
||||
The vector value of this point's velocity in the frame
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> p1 = Point('p1')
|
||||
>>> p1.set_vel(N, 10 * N.x)
|
||||
>>> p1.vel(N)
|
||||
10*N.x
|
||||
|
||||
"""
|
||||
|
||||
if value == 0:
|
||||
value = Vector(0)
|
||||
value = _check_vector(value)
|
||||
_check_frame(frame)
|
||||
self._vel_dict.update({frame: value})
|
||||
|
||||
def v1pt_theory(self, otherpoint, outframe, interframe):
|
||||
"""Sets the velocity of this point with the 1-point theory.
|
||||
|
||||
The 1-point theory for point velocity looks like this:
|
||||
|
||||
^N v^P = ^B v^P + ^N v^O + ^N omega^B x r^OP
|
||||
|
||||
where O is a point fixed in B, P is a point moving in B, and B is
|
||||
rotating in frame N.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
otherpoint : Point
|
||||
The first point of the 1-point theory (O)
|
||||
outframe : ReferenceFrame
|
||||
The frame we want this point's velocity defined in (N)
|
||||
interframe : ReferenceFrame
|
||||
The intermediate frame in this calculation (B)
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame
|
||||
>>> from sympy.physics.vector import dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> q = dynamicsymbols('q')
|
||||
>>> q2 = dynamicsymbols('q2')
|
||||
>>> qd = dynamicsymbols('q', 1)
|
||||
>>> q2d = dynamicsymbols('q2', 1)
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> B = ReferenceFrame('B')
|
||||
>>> B.set_ang_vel(N, 5 * B.y)
|
||||
>>> O = Point('O')
|
||||
>>> P = O.locatenew('P', q * B.x + q2 * B.y)
|
||||
>>> P.set_vel(B, qd * B.x + q2d * B.y)
|
||||
>>> O.set_vel(N, 0)
|
||||
>>> P.v1pt_theory(O, N, B)
|
||||
q'*B.x + q2'*B.y - 5*q*B.z
|
||||
|
||||
"""
|
||||
|
||||
_check_frame(outframe)
|
||||
_check_frame(interframe)
|
||||
self._check_point(otherpoint)
|
||||
dist = self.pos_from(otherpoint)
|
||||
v1 = self.vel(interframe)
|
||||
v2 = otherpoint.vel(outframe)
|
||||
omega = interframe.ang_vel_in(outframe)
|
||||
self.set_vel(outframe, v1 + v2 + (omega.cross(dist)))
|
||||
return self.vel(outframe)
|
||||
|
||||
def v2pt_theory(self, otherpoint, outframe, fixedframe):
|
||||
"""Sets the velocity of this point with the 2-point theory.
|
||||
|
||||
The 2-point theory for point velocity looks like this:
|
||||
|
||||
^N v^P = ^N v^O + ^N omega^B x r^OP
|
||||
|
||||
where O and P are both points fixed in frame B, which is rotating in
|
||||
frame N.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
otherpoint : Point
|
||||
The first point of the 2-point theory (O)
|
||||
outframe : ReferenceFrame
|
||||
The frame we want this point's velocity defined in (N)
|
||||
fixedframe : ReferenceFrame
|
||||
The frame in which both points are fixed (B)
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> q = dynamicsymbols('q')
|
||||
>>> qd = dynamicsymbols('q', 1)
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> B = N.orientnew('B', 'Axis', [q, N.z])
|
||||
>>> O = Point('O')
|
||||
>>> P = O.locatenew('P', 10 * B.x)
|
||||
>>> O.set_vel(N, 5 * N.x)
|
||||
>>> P.v2pt_theory(O, N, B)
|
||||
5*N.x + 10*q'*B.y
|
||||
|
||||
"""
|
||||
|
||||
_check_frame(outframe)
|
||||
_check_frame(fixedframe)
|
||||
self._check_point(otherpoint)
|
||||
dist = self.pos_from(otherpoint)
|
||||
v = otherpoint.vel(outframe)
|
||||
omega = fixedframe.ang_vel_in(outframe)
|
||||
self.set_vel(outframe, v + (omega.cross(dist)))
|
||||
return self.vel(outframe)
|
||||
|
||||
def vel(self, frame):
|
||||
"""The velocity Vector of this Point in the ReferenceFrame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame in which the returned velocity vector will be defined in
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> p1 = Point('p1')
|
||||
>>> p1.set_vel(N, 10 * N.x)
|
||||
>>> p1.vel(N)
|
||||
10*N.x
|
||||
|
||||
Velocities will be automatically calculated if possible, otherwise a
|
||||
``ValueError`` will be returned. If it is possible to calculate
|
||||
multiple different velocities from the relative points, the points
|
||||
defined most directly relative to this point will be used. In the case
|
||||
of inconsistent relative positions of points, incorrect velocities may
|
||||
be returned. It is up to the user to define prior relative positions
|
||||
and velocities of points in a self-consistent way.
|
||||
|
||||
>>> p = Point('p')
|
||||
>>> q = dynamicsymbols('q')
|
||||
>>> p.set_vel(N, 10 * N.x)
|
||||
>>> p2 = Point('p2')
|
||||
>>> p2.set_pos(p, q*N.x)
|
||||
>>> p2.vel(N)
|
||||
(Derivative(q(t), t) + 10)*N.x
|
||||
|
||||
"""
|
||||
|
||||
_check_frame(frame)
|
||||
if not (frame in self._vel_dict):
|
||||
valid_neighbor_found = False
|
||||
is_cyclic = False
|
||||
visited = []
|
||||
queue = [self]
|
||||
candidate_neighbor = []
|
||||
while queue: # BFS to find nearest point
|
||||
node = queue.pop(0)
|
||||
if node not in visited:
|
||||
visited.append(node)
|
||||
for neighbor, neighbor_pos in node._pos_dict.items():
|
||||
if neighbor in visited:
|
||||
continue
|
||||
try:
|
||||
# Checks if pos vector is valid
|
||||
neighbor_pos.express(frame)
|
||||
except ValueError:
|
||||
continue
|
||||
if neighbor in queue:
|
||||
is_cyclic = True
|
||||
try:
|
||||
# Checks if point has its vel defined in req frame
|
||||
neighbor_velocity = neighbor._vel_dict[frame]
|
||||
except KeyError:
|
||||
queue.append(neighbor)
|
||||
continue
|
||||
candidate_neighbor.append(neighbor)
|
||||
if not valid_neighbor_found:
|
||||
self.set_vel(frame, self.pos_from(neighbor).dt(frame) + neighbor_velocity)
|
||||
valid_neighbor_found = True
|
||||
if is_cyclic:
|
||||
warn(filldedent("""
|
||||
Kinematic loops are defined among the positions of points. This
|
||||
is likely not desired and may cause errors in your calculations.
|
||||
"""))
|
||||
if len(candidate_neighbor) > 1:
|
||||
warn(filldedent(f"""
|
||||
Velocity of {self.name} automatically calculated based on point
|
||||
{candidate_neighbor[0].name} but it is also possible from
|
||||
points(s): {str(candidate_neighbor[1:])}. Velocities from these
|
||||
points are not necessarily the same. This may cause errors in
|
||||
your calculations."""))
|
||||
if valid_neighbor_found:
|
||||
return self._vel_dict[frame]
|
||||
else:
|
||||
raise ValueError(filldedent(f"""
|
||||
Velocity of point {self.name} has not been defined in
|
||||
ReferenceFrame {frame.name}."""))
|
||||
|
||||
return self._vel_dict[frame]
|
||||
|
||||
def partial_velocity(self, frame, *gen_speeds):
|
||||
"""Returns the partial velocities of the linear velocity vector of this
|
||||
point in the given frame with respect to one or more provided
|
||||
generalized speeds.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
frame : ReferenceFrame
|
||||
The frame with which the velocity is defined in.
|
||||
gen_speeds : functions of time
|
||||
The generalized speeds.
|
||||
|
||||
Returns
|
||||
=======
|
||||
partial_velocities : tuple of Vector
|
||||
The partial velocity vectors corresponding to the provided
|
||||
generalized speeds.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, Point
|
||||
>>> from sympy.physics.vector import dynamicsymbols
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> A = ReferenceFrame('A')
|
||||
>>> p = Point('p')
|
||||
>>> u1, u2 = dynamicsymbols('u1, u2')
|
||||
>>> p.set_vel(N, u1 * N.x + u2 * A.y)
|
||||
>>> p.partial_velocity(N, u1)
|
||||
N.x
|
||||
>>> p.partial_velocity(N, u1, u2)
|
||||
(N.x, A.y)
|
||||
|
||||
"""
|
||||
|
||||
from sympy.physics.vector.functions import partial_velocity
|
||||
|
||||
vel = self.vel(frame)
|
||||
partials = partial_velocity([vel], gen_speeds, frame)[0]
|
||||
|
||||
if len(partials) == 1:
|
||||
return partials[0]
|
||||
else:
|
||||
return tuple(partials)
|
||||
@@ -0,0 +1,371 @@
|
||||
from sympy.core.function import Derivative
|
||||
from sympy.core.function import UndefinedFunction, AppliedUndef
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.interactive.printing import init_printing
|
||||
from sympy.printing.latex import LatexPrinter
|
||||
from sympy.printing.pretty.pretty import PrettyPrinter
|
||||
from sympy.printing.pretty.pretty_symbology import center_accent
|
||||
from sympy.printing.str import StrPrinter
|
||||
from sympy.printing.precedence import PRECEDENCE
|
||||
|
||||
__all__ = ['vprint', 'vsstrrepr', 'vsprint', 'vpprint', 'vlatex',
|
||||
'init_vprinting']
|
||||
|
||||
|
||||
class VectorStrPrinter(StrPrinter):
|
||||
"""String Printer for vector expressions. """
|
||||
|
||||
def _print_Derivative(self, e):
|
||||
from sympy.physics.vector.functions import dynamicsymbols
|
||||
t = dynamicsymbols._t
|
||||
if (bool(sum(i == t for i in e.variables)) &
|
||||
isinstance(type(e.args[0]), UndefinedFunction)):
|
||||
ol = str(e.args[0].func)
|
||||
for i, v in enumerate(e.variables):
|
||||
ol += dynamicsymbols._str
|
||||
return ol
|
||||
else:
|
||||
return StrPrinter().doprint(e)
|
||||
|
||||
def _print_Function(self, e):
|
||||
from sympy.physics.vector.functions import dynamicsymbols
|
||||
t = dynamicsymbols._t
|
||||
if isinstance(type(e), UndefinedFunction):
|
||||
return StrPrinter().doprint(e).replace("(%s)" % t, '')
|
||||
return e.func.__name__ + "(%s)" % self.stringify(e.args, ", ")
|
||||
|
||||
|
||||
class VectorStrReprPrinter(VectorStrPrinter):
|
||||
"""String repr printer for vector expressions."""
|
||||
def _print_str(self, s):
|
||||
return repr(s)
|
||||
|
||||
|
||||
class VectorLatexPrinter(LatexPrinter):
|
||||
"""Latex Printer for vector expressions. """
|
||||
|
||||
def _print_Function(self, expr, exp=None):
|
||||
from sympy.physics.vector.functions import dynamicsymbols
|
||||
func = expr.func.__name__
|
||||
t = dynamicsymbols._t
|
||||
|
||||
if (hasattr(self, '_print_' + func) and not
|
||||
isinstance(type(expr), UndefinedFunction)):
|
||||
return getattr(self, '_print_' + func)(expr, exp)
|
||||
elif isinstance(type(expr), UndefinedFunction) and (expr.args == (t,)):
|
||||
# treat this function like a symbol
|
||||
expr = Symbol(func)
|
||||
if exp is not None:
|
||||
# copied from LatexPrinter._helper_print_standard_power, which
|
||||
# we can't call because we only have exp as a string.
|
||||
base = self.parenthesize(expr, PRECEDENCE['Pow'])
|
||||
base = self.parenthesize_super(base)
|
||||
return r"%s^{%s}" % (base, exp)
|
||||
else:
|
||||
return super()._print(expr)
|
||||
else:
|
||||
return super()._print_Function(expr, exp)
|
||||
|
||||
def _print_Derivative(self, der_expr):
|
||||
from sympy.physics.vector.functions import dynamicsymbols
|
||||
# make sure it is in the right form
|
||||
der_expr = der_expr.doit()
|
||||
if not isinstance(der_expr, Derivative):
|
||||
return r"\left(%s\right)" % self.doprint(der_expr)
|
||||
|
||||
# check if expr is a dynamicsymbol
|
||||
t = dynamicsymbols._t
|
||||
expr = der_expr.expr
|
||||
red = expr.atoms(AppliedUndef)
|
||||
syms = der_expr.variables
|
||||
test1 = not all(True for i in red if i.free_symbols == {t})
|
||||
test2 = not all(t == i for i in syms)
|
||||
if test1 or test2:
|
||||
return super()._print_Derivative(der_expr)
|
||||
|
||||
# done checking
|
||||
dots = len(syms)
|
||||
base = self._print_Function(expr)
|
||||
base_split = base.split('_', 1)
|
||||
base = base_split[0]
|
||||
if dots == 1:
|
||||
base = r"\dot{%s}" % base
|
||||
elif dots == 2:
|
||||
base = r"\ddot{%s}" % base
|
||||
elif dots == 3:
|
||||
base = r"\dddot{%s}" % base
|
||||
elif dots == 4:
|
||||
base = r"\ddddot{%s}" % base
|
||||
else: # Fallback to standard printing
|
||||
return super()._print_Derivative(der_expr)
|
||||
if len(base_split) != 1:
|
||||
base += '_' + base_split[1]
|
||||
return base
|
||||
|
||||
|
||||
class VectorPrettyPrinter(PrettyPrinter):
|
||||
"""Pretty Printer for vectorialexpressions. """
|
||||
|
||||
def _print_Derivative(self, deriv):
|
||||
from sympy.physics.vector.functions import dynamicsymbols
|
||||
# XXX use U('PARTIAL DIFFERENTIAL') here ?
|
||||
t = dynamicsymbols._t
|
||||
dot_i = 0
|
||||
syms = list(reversed(deriv.variables))
|
||||
|
||||
while len(syms) > 0:
|
||||
if syms[-1] == t:
|
||||
syms.pop()
|
||||
dot_i += 1
|
||||
else:
|
||||
return super()._print_Derivative(deriv)
|
||||
|
||||
if not (isinstance(type(deriv.expr), UndefinedFunction) and
|
||||
(deriv.expr.args == (t,))):
|
||||
return super()._print_Derivative(deriv)
|
||||
else:
|
||||
pform = self._print_Function(deriv.expr)
|
||||
|
||||
# the following condition would happen with some sort of non-standard
|
||||
# dynamic symbol I guess, so we'll just print the SymPy way
|
||||
if len(pform.picture) > 1:
|
||||
return super()._print_Derivative(deriv)
|
||||
|
||||
# There are only special symbols up to fourth-order derivatives
|
||||
if dot_i >= 5:
|
||||
return super()._print_Derivative(deriv)
|
||||
|
||||
# Deal with special symbols
|
||||
dots = {0: "",
|
||||
1: "\N{COMBINING DOT ABOVE}",
|
||||
2: "\N{COMBINING DIAERESIS}",
|
||||
3: "\N{COMBINING THREE DOTS ABOVE}",
|
||||
4: "\N{COMBINING FOUR DOTS ABOVE}"}
|
||||
|
||||
d = pform.__dict__
|
||||
# if unicode is false then calculate number of apostrophes needed and
|
||||
# add to output
|
||||
if not self._use_unicode:
|
||||
apostrophes = ""
|
||||
for i in range(0, dot_i):
|
||||
apostrophes += "'"
|
||||
d['picture'][0] += apostrophes + "(t)"
|
||||
else:
|
||||
d['picture'] = [center_accent(d['picture'][0], dots[dot_i])]
|
||||
return pform
|
||||
|
||||
def _print_Function(self, e):
|
||||
from sympy.physics.vector.functions import dynamicsymbols
|
||||
t = dynamicsymbols._t
|
||||
# XXX works only for applied functions
|
||||
func = e.func
|
||||
args = e.args
|
||||
func_name = func.__name__
|
||||
pform = self._print_Symbol(Symbol(func_name))
|
||||
# If this function is an Undefined function of t, it is probably a
|
||||
# dynamic symbol, so we'll skip the (t). The rest of the code is
|
||||
# identical to the normal PrettyPrinter code
|
||||
if not (isinstance(func, UndefinedFunction) and (args == (t,))):
|
||||
return super()._print_Function(e)
|
||||
return pform
|
||||
|
||||
|
||||
def vprint(expr, **settings):
|
||||
r"""Function for printing of expressions generated in the
|
||||
sympy.physics vector package.
|
||||
|
||||
Extends SymPy's StrPrinter, takes the same setting accepted by SymPy's
|
||||
:func:`~.sstr`, and is equivalent to ``print(sstr(foo))``.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : valid SymPy object
|
||||
SymPy expression to print.
|
||||
settings : args
|
||||
Same as the settings accepted by SymPy's sstr().
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import vprint, dynamicsymbols
|
||||
>>> u1 = dynamicsymbols('u1')
|
||||
>>> print(u1)
|
||||
u1(t)
|
||||
>>> vprint(u1)
|
||||
u1
|
||||
|
||||
"""
|
||||
|
||||
outstr = vsprint(expr, **settings)
|
||||
|
||||
import builtins
|
||||
if (outstr != 'None'):
|
||||
builtins._ = outstr
|
||||
print(outstr)
|
||||
|
||||
|
||||
def vsstrrepr(expr, **settings):
|
||||
"""Function for displaying expression representation's with vector
|
||||
printing enabled.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : valid SymPy object
|
||||
SymPy expression to print.
|
||||
settings : args
|
||||
Same as the settings accepted by SymPy's sstrrepr().
|
||||
|
||||
"""
|
||||
p = VectorStrReprPrinter(settings)
|
||||
return p.doprint(expr)
|
||||
|
||||
|
||||
def vsprint(expr, **settings):
|
||||
r"""Function for displaying expressions generated in the
|
||||
sympy.physics vector package.
|
||||
|
||||
Returns the output of vprint() as a string.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : valid SymPy object
|
||||
SymPy expression to print
|
||||
settings : args
|
||||
Same as the settings accepted by SymPy's sstr().
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import vsprint, dynamicsymbols
|
||||
>>> u1, u2 = dynamicsymbols('u1 u2')
|
||||
>>> u2d = dynamicsymbols('u2', level=1)
|
||||
>>> print("%s = %s" % (u1, u2 + u2d))
|
||||
u1(t) = u2(t) + Derivative(u2(t), t)
|
||||
>>> print("%s = %s" % (vsprint(u1), vsprint(u2 + u2d)))
|
||||
u1 = u2 + u2'
|
||||
|
||||
"""
|
||||
|
||||
string_printer = VectorStrPrinter(settings)
|
||||
return string_printer.doprint(expr)
|
||||
|
||||
|
||||
def vpprint(expr, **settings):
|
||||
r"""Function for pretty printing of expressions generated in the
|
||||
sympy.physics vector package.
|
||||
|
||||
Mainly used for expressions not inside a vector; the output of running
|
||||
scripts and generating equations of motion. Takes the same options as
|
||||
SymPy's :func:`~.pretty_print`; see that function for more information.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : valid SymPy object
|
||||
SymPy expression to pretty print
|
||||
settings : args
|
||||
Same as those accepted by SymPy's pretty_print.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
pp = VectorPrettyPrinter(settings)
|
||||
|
||||
# Note that this is copied from sympy.printing.pretty.pretty_print:
|
||||
|
||||
# XXX: this is an ugly hack, but at least it works
|
||||
use_unicode = pp._settings['use_unicode']
|
||||
from sympy.printing.pretty.pretty_symbology import pretty_use_unicode
|
||||
uflag = pretty_use_unicode(use_unicode)
|
||||
|
||||
try:
|
||||
return pp.doprint(expr)
|
||||
finally:
|
||||
pretty_use_unicode(uflag)
|
||||
|
||||
|
||||
def vlatex(expr, **settings):
|
||||
r"""Function for printing latex representation of sympy.physics.vector
|
||||
objects.
|
||||
|
||||
For latex representation of Vectors, Dyadics, and dynamicsymbols. Takes the
|
||||
same options as SymPy's :func:`~.latex`; see that function for more
|
||||
information;
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : valid SymPy object
|
||||
SymPy expression to represent in LaTeX form
|
||||
settings : args
|
||||
Same as latex()
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import vlatex, ReferenceFrame, dynamicsymbols
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> q1, q2 = dynamicsymbols('q1 q2')
|
||||
>>> q1d, q2d = dynamicsymbols('q1 q2', 1)
|
||||
>>> q1dd, q2dd = dynamicsymbols('q1 q2', 2)
|
||||
>>> vlatex(N.x + N.y)
|
||||
'\\mathbf{\\hat{n}_x} + \\mathbf{\\hat{n}_y}'
|
||||
>>> vlatex(q1 + q2)
|
||||
'q_{1} + q_{2}'
|
||||
>>> vlatex(q1d)
|
||||
'\\dot{q}_{1}'
|
||||
>>> vlatex(q1 * q2d)
|
||||
'q_{1} \\dot{q}_{2}'
|
||||
>>> vlatex(q1dd * q1 / q1d)
|
||||
'\\frac{q_{1} \\ddot{q}_{1}}{\\dot{q}_{1}}'
|
||||
|
||||
"""
|
||||
latex_printer = VectorLatexPrinter(settings)
|
||||
|
||||
return latex_printer.doprint(expr)
|
||||
|
||||
|
||||
def init_vprinting(**kwargs):
|
||||
"""Initializes time derivative printing for all SymPy objects, i.e. any
|
||||
functions of time will be displayed in a more compact notation. The main
|
||||
benefit of this is for printing of time derivatives; instead of
|
||||
displaying as ``Derivative(f(t),t)``, it will display ``f'``. This is
|
||||
only actually needed for when derivatives are present and are not in a
|
||||
physics.vector.Vector or physics.vector.Dyadic object. This function is a
|
||||
light wrapper to :func:`~.init_printing`. Any keyword
|
||||
arguments for it are valid here.
|
||||
|
||||
{0}
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Function, symbols
|
||||
>>> t, x = symbols('t, x')
|
||||
>>> omega = Function('omega')
|
||||
>>> omega(x).diff()
|
||||
Derivative(omega(x), x)
|
||||
>>> omega(t).diff()
|
||||
Derivative(omega(t), t)
|
||||
|
||||
Now use the string printer:
|
||||
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> omega(x).diff()
|
||||
Derivative(omega(x), x)
|
||||
>>> omega(t).diff()
|
||||
omega'
|
||||
|
||||
"""
|
||||
kwargs['str_printer'] = vsstrrepr
|
||||
kwargs['pretty_printer'] = vpprint
|
||||
kwargs['latex_printer'] = vlatex
|
||||
init_printing(**kwargs)
|
||||
|
||||
|
||||
params = init_printing.__doc__.split('Examples\n ========')[0] # type: ignore
|
||||
init_vprinting.__doc__ = init_vprinting.__doc__.format(params) # type: ignore
|
||||
@@ -0,0 +1,123 @@
|
||||
from sympy.core.numbers import (Float, pi)
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
||||
from sympy.physics.vector import ReferenceFrame, dynamicsymbols, outer
|
||||
from sympy.physics.vector.dyadic import _check_dyadic
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
A = ReferenceFrame('A')
|
||||
|
||||
|
||||
def test_dyadic():
|
||||
d1 = A.x | A.x
|
||||
d2 = A.y | A.y
|
||||
d3 = A.x | A.y
|
||||
assert d1 * 0 == 0
|
||||
assert d1 != 0
|
||||
assert d1 * 2 == 2 * A.x | A.x
|
||||
assert d1 / 2. == 0.5 * d1
|
||||
assert d1 & (0 * d1) == 0
|
||||
assert d1 & d2 == 0
|
||||
assert d1 & A.x == A.x
|
||||
assert d1 ^ A.x == 0
|
||||
assert d1 ^ A.y == A.x | A.z
|
||||
assert d1 ^ A.z == - A.x | A.y
|
||||
assert d2 ^ A.x == - A.y | A.z
|
||||
assert A.x ^ d1 == 0
|
||||
assert A.y ^ d1 == - A.z | A.x
|
||||
assert A.z ^ d1 == A.y | A.x
|
||||
assert A.x & d1 == A.x
|
||||
assert A.y & d1 == 0
|
||||
assert A.y & d2 == A.y
|
||||
assert d1 & d3 == A.x | A.y
|
||||
assert d3 & d1 == 0
|
||||
assert d1.dt(A) == 0
|
||||
q = dynamicsymbols('q')
|
||||
qd = dynamicsymbols('q', 1)
|
||||
B = A.orientnew('B', 'Axis', [q, A.z])
|
||||
assert d1.express(B) == d1.express(B, B)
|
||||
assert d1.express(B) == ((cos(q)**2) * (B.x | B.x) + (-sin(q) * cos(q)) *
|
||||
(B.x | B.y) + (-sin(q) * cos(q)) * (B.y | B.x) + (sin(q)**2) *
|
||||
(B.y | B.y))
|
||||
assert d1.express(B, A) == (cos(q)) * (B.x | A.x) + (-sin(q)) * (B.y | A.x)
|
||||
assert d1.express(A, B) == (cos(q)) * (A.x | B.x) + (-sin(q)) * (A.x | B.y)
|
||||
assert d1.dt(B) == (-qd) * (A.y | A.x) + (-qd) * (A.x | A.y)
|
||||
|
||||
assert d1.to_matrix(A) == Matrix([[1, 0, 0], [0, 0, 0], [0, 0, 0]])
|
||||
assert d1.to_matrix(A, B) == Matrix([[cos(q), -sin(q), 0],
|
||||
[0, 0, 0],
|
||||
[0, 0, 0]])
|
||||
assert d3.to_matrix(A) == Matrix([[0, 1, 0], [0, 0, 0], [0, 0, 0]])
|
||||
a, b, c, d, e, f = symbols('a, b, c, d, e, f')
|
||||
v1 = a * A.x + b * A.y + c * A.z
|
||||
v2 = d * A.x + e * A.y + f * A.z
|
||||
d4 = v1.outer(v2)
|
||||
assert d4.to_matrix(A) == Matrix([[a * d, a * e, a * f],
|
||||
[b * d, b * e, b * f],
|
||||
[c * d, c * e, c * f]])
|
||||
d5 = v1.outer(v1)
|
||||
C = A.orientnew('C', 'Axis', [q, A.x])
|
||||
for expected, actual in zip(C.dcm(A) * d5.to_matrix(A) * C.dcm(A).T,
|
||||
d5.to_matrix(C)):
|
||||
assert (expected - actual).simplify() == 0
|
||||
|
||||
raises(TypeError, lambda: d1.applyfunc(0))
|
||||
|
||||
|
||||
def test_dyadic_simplify():
|
||||
x, y, z, k, n, m, w, f, s, A = symbols('x, y, z, k, n, m, w, f, s, A')
|
||||
N = ReferenceFrame('N')
|
||||
|
||||
dy = N.x | N.x
|
||||
test1 = (1 / x + 1 / y) * dy
|
||||
assert (N.x & test1 & N.x) != (x + y) / (x * y)
|
||||
test1 = test1.simplify()
|
||||
assert (N.x & test1 & N.x) == (x + y) / (x * y)
|
||||
|
||||
test2 = (A**2 * s**4 / (4 * pi * k * m**3)) * dy
|
||||
test2 = test2.simplify()
|
||||
assert (N.x & test2 & N.x) == (A**2 * s**4 / (4 * pi * k * m**3))
|
||||
|
||||
test3 = ((4 + 4 * x - 2 * (2 + 2 * x)) / (2 + 2 * x)) * dy
|
||||
test3 = test3.simplify()
|
||||
assert (N.x & test3 & N.x) == 0
|
||||
|
||||
test4 = ((-4 * x * y**2 - 2 * y**3 - 2 * x**2 * y) / (x + y)**2) * dy
|
||||
test4 = test4.simplify()
|
||||
assert (N.x & test4 & N.x) == -2 * y
|
||||
|
||||
|
||||
def test_dyadic_subs():
|
||||
N = ReferenceFrame('N')
|
||||
s = symbols('s')
|
||||
a = s*(N.x | N.x)
|
||||
assert a.subs({s: 2}) == 2*(N.x | N.x)
|
||||
|
||||
|
||||
def test_check_dyadic():
|
||||
raises(TypeError, lambda: _check_dyadic(0))
|
||||
|
||||
|
||||
def test_dyadic_evalf():
|
||||
N = ReferenceFrame('N')
|
||||
a = pi * (N.x | N.x)
|
||||
assert a.evalf(3) == Float('3.1416', 3) * (N.x | N.x)
|
||||
s = symbols('s')
|
||||
a = 5 * s * pi* (N.x | N.x)
|
||||
assert a.evalf(2) == Float('5', 2) * Float('3.1416', 2) * s * (N.x | N.x)
|
||||
assert a.evalf(9, subs={s: 5.124}) == Float('80.48760378', 9) * (N.x | N.x)
|
||||
|
||||
|
||||
def test_dyadic_xreplace():
|
||||
x, y, z = symbols('x y z')
|
||||
N = ReferenceFrame('N')
|
||||
D = outer(N.x, N.x)
|
||||
v = x*y * D
|
||||
assert v.xreplace({x : cos(x)}) == cos(x)*y * D
|
||||
assert v.xreplace({x*y : pi}) == pi * D
|
||||
v = (x*y)**z * D
|
||||
assert v.xreplace({(x*y)**z : 1}) == D
|
||||
assert v.xreplace({x:1, z:0}) == D
|
||||
raises(TypeError, lambda: v.xreplace())
|
||||
raises(TypeError, lambda: v.xreplace([x, y]))
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.physics.vector import ReferenceFrame, Vector, Point, \
|
||||
dynamicsymbols
|
||||
from sympy.physics.vector.fieldfunctions import divergence, \
|
||||
gradient, curl, is_conservative, is_solenoidal, \
|
||||
scalar_potential, scalar_potential_difference
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
R = ReferenceFrame('R')
|
||||
q = dynamicsymbols('q')
|
||||
P = R.orientnew('P', 'Axis', [q, R.z])
|
||||
|
||||
|
||||
def test_curl():
|
||||
assert curl(Vector(0), R) == Vector(0)
|
||||
assert curl(R.x, R) == Vector(0)
|
||||
assert curl(2*R[1]**2*R.y, R) == Vector(0)
|
||||
assert curl(R[0]*R[1]*R.z, R) == R[0]*R.x - R[1]*R.y
|
||||
assert curl(R[0]*R[1]*R[2] * (R.x+R.y+R.z), R) == \
|
||||
(-R[0]*R[1] + R[0]*R[2])*R.x + (R[0]*R[1] - R[1]*R[2])*R.y + \
|
||||
(-R[0]*R[2] + R[1]*R[2])*R.z
|
||||
assert curl(2*R[0]**2*R.y, R) == 4*R[0]*R.z
|
||||
assert curl(P[0]**2*R.x + P.y, R) == \
|
||||
- 2*(R[0]*cos(q) + R[1]*sin(q))*sin(q)*R.z
|
||||
assert curl(P[0]*R.y, P) == cos(q)*P.z
|
||||
|
||||
|
||||
def test_divergence():
|
||||
assert divergence(Vector(0), R) is S.Zero
|
||||
assert divergence(R.x, R) is S.Zero
|
||||
assert divergence(R[0]**2*R.x, R) == 2*R[0]
|
||||
assert divergence(R[0]*R[1]*R[2] * (R.x+R.y+R.z), R) == \
|
||||
R[0]*R[1] + R[0]*R[2] + R[1]*R[2]
|
||||
assert divergence((1/(R[0]*R[1]*R[2])) * (R.x+R.y+R.z), R) == \
|
||||
-1/(R[0]*R[1]*R[2]**2) - 1/(R[0]*R[1]**2*R[2]) - \
|
||||
1/(R[0]**2*R[1]*R[2])
|
||||
v = P[0]*P.x + P[1]*P.y + P[2]*P.z
|
||||
assert divergence(v, P) == 3
|
||||
assert divergence(v, R).simplify() == 3
|
||||
assert divergence(P[0]*R.x + R[0]*P.x, R) == 2*cos(q)
|
||||
|
||||
|
||||
def test_gradient():
|
||||
a = Symbol('a')
|
||||
assert gradient(0, R) == Vector(0)
|
||||
assert gradient(R[0], R) == R.x
|
||||
assert gradient(R[0]*R[1]*R[2], R) == \
|
||||
R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z
|
||||
assert gradient(2*R[0]**2, R) == 4*R[0]*R.x
|
||||
assert gradient(a*sin(R[1])/R[0], R) == \
|
||||
- a*sin(R[1])/R[0]**2*R.x + a*cos(R[1])/R[0]*R.y
|
||||
assert gradient(P[0]*P[1], R) == \
|
||||
((-R[0]*sin(q) + R[1]*cos(q))*cos(q) - (R[0]*cos(q) + R[1]*sin(q))*sin(q))*R.x + \
|
||||
((-R[0]*sin(q) + R[1]*cos(q))*sin(q) + (R[0]*cos(q) + R[1]*sin(q))*cos(q))*R.y
|
||||
assert gradient(P[0]*R[2], P) == P[2]*P.x + P[0]*P.z
|
||||
|
||||
|
||||
scalar_field = 2*R[0]**2*R[1]*R[2]
|
||||
grad_field = gradient(scalar_field, R)
|
||||
vector_field = R[1]**2*R.x + 3*R[0]*R.y + 5*R[1]*R[2]*R.z
|
||||
curl_field = curl(vector_field, R)
|
||||
|
||||
|
||||
def test_conservative():
|
||||
assert is_conservative(0) is True
|
||||
assert is_conservative(R.x) is True
|
||||
assert is_conservative(2 * R.x + 3 * R.y + 4 * R.z) is True
|
||||
assert is_conservative(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z) is \
|
||||
True
|
||||
assert is_conservative(R[0] * R.y) is False
|
||||
assert is_conservative(grad_field) is True
|
||||
assert is_conservative(curl_field) is False
|
||||
assert is_conservative(4*R[0]*R[1]*R[2]*R.x + 2*R[0]**2*R[2]*R.y) is \
|
||||
False
|
||||
assert is_conservative(R[2]*P.x + P[0]*R.z) is True
|
||||
|
||||
|
||||
def test_solenoidal():
|
||||
assert is_solenoidal(0) is True
|
||||
assert is_solenoidal(R.x) is True
|
||||
assert is_solenoidal(2 * R.x + 3 * R.y + 4 * R.z) is True
|
||||
assert is_solenoidal(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z) is \
|
||||
True
|
||||
assert is_solenoidal(R[1] * R.y) is False
|
||||
assert is_solenoidal(grad_field) is False
|
||||
assert is_solenoidal(curl_field) is True
|
||||
assert is_solenoidal((-2*R[1] + 3)*R.z) is True
|
||||
assert is_solenoidal(cos(q)*R.x + sin(q)*R.y + cos(q)*P.z) is True
|
||||
assert is_solenoidal(R[2]*P.x + P[0]*R.z) is True
|
||||
|
||||
|
||||
def test_scalar_potential():
|
||||
assert scalar_potential(0, R) == 0
|
||||
assert scalar_potential(R.x, R) == R[0]
|
||||
assert scalar_potential(R.y, R) == R[1]
|
||||
assert scalar_potential(R.z, R) == R[2]
|
||||
assert scalar_potential(R[1]*R[2]*R.x + R[0]*R[2]*R.y + \
|
||||
R[0]*R[1]*R.z, R) == R[0]*R[1]*R[2]
|
||||
assert scalar_potential(grad_field, R) == scalar_field
|
||||
assert scalar_potential(R[2]*P.x + P[0]*R.z, R) == \
|
||||
R[0]*R[2]*cos(q) + R[1]*R[2]*sin(q)
|
||||
assert scalar_potential(R[2]*P.x + P[0]*R.z, P) == P[0]*P[2]
|
||||
raises(ValueError, lambda: scalar_potential(R[0] * R.y, R))
|
||||
|
||||
|
||||
def test_scalar_potential_difference():
|
||||
origin = Point('O')
|
||||
point1 = origin.locatenew('P1', 1*R.x + 2*R.y + 3*R.z)
|
||||
point2 = origin.locatenew('P2', 4*R.x + 5*R.y + 6*R.z)
|
||||
genericpointR = origin.locatenew('RP', R[0]*R.x + R[1]*R.y + R[2]*R.z)
|
||||
genericpointP = origin.locatenew('PP', P[0]*P.x + P[1]*P.y + P[2]*P.z)
|
||||
assert scalar_potential_difference(S.Zero, R, point1, point2, \
|
||||
origin) == 0
|
||||
assert scalar_potential_difference(scalar_field, R, origin, \
|
||||
genericpointR, origin) == \
|
||||
scalar_field
|
||||
assert scalar_potential_difference(grad_field, R, origin, \
|
||||
genericpointR, origin) == \
|
||||
scalar_field
|
||||
assert scalar_potential_difference(grad_field, R, point1, point2,
|
||||
origin) == 948
|
||||
assert scalar_potential_difference(R[1]*R[2]*R.x + R[0]*R[2]*R.y + \
|
||||
R[0]*R[1]*R.z, R, point1,
|
||||
genericpointR, origin) == \
|
||||
R[0]*R[1]*R[2] - 6
|
||||
potential_diff_P = 2*P[2]*(P[0]*sin(q) + P[1]*cos(q))*\
|
||||
(P[0]*cos(q) - P[1]*sin(q))**2
|
||||
assert scalar_potential_difference(grad_field, P, origin, \
|
||||
genericpointP, \
|
||||
origin).simplify() == \
|
||||
potential_diff_P
|
||||
@@ -0,0 +1,761 @@
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.simplify import trigsimp
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.matrices.dense import (eye, zeros)
|
||||
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.physics.vector import (ReferenceFrame, Vector, CoordinateSym,
|
||||
dynamicsymbols, time_derivative, express,
|
||||
dot)
|
||||
from sympy.physics.vector.frame import _check_frame
|
||||
from sympy.physics.vector.vector import VectorTypeError
|
||||
from sympy.testing.pytest import raises
|
||||
import warnings
|
||||
import pickle
|
||||
|
||||
|
||||
def test_dict_list():
|
||||
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
C = ReferenceFrame('C')
|
||||
D = ReferenceFrame('D')
|
||||
E = ReferenceFrame('E')
|
||||
F = ReferenceFrame('F')
|
||||
|
||||
B.orient_axis(A, A.x, 1.0)
|
||||
C.orient_axis(B, B.x, 1.0)
|
||||
D.orient_axis(C, C.x, 1.0)
|
||||
|
||||
assert D._dict_list(A, 0) == [D, C, B, A]
|
||||
|
||||
E.orient_axis(D, D.x, 1.0)
|
||||
|
||||
assert C._dict_list(A, 0) == [C, B, A]
|
||||
assert C._dict_list(E, 0) == [C, D, E]
|
||||
|
||||
# only 0, 1, 2 permitted for second argument
|
||||
raises(ValueError, lambda: C._dict_list(E, 5))
|
||||
# no connecting path
|
||||
raises(ValueError, lambda: F._dict_list(A, 0))
|
||||
|
||||
|
||||
def test_coordinate_vars():
|
||||
"""Tests the coordinate variables functionality"""
|
||||
A = ReferenceFrame('A')
|
||||
assert CoordinateSym('Ax', A, 0) == A[0]
|
||||
assert CoordinateSym('Ax', A, 1) == A[1]
|
||||
assert CoordinateSym('Ax', A, 2) == A[2]
|
||||
raises(ValueError, lambda: CoordinateSym('Ax', A, 3))
|
||||
q = dynamicsymbols('q')
|
||||
qd = dynamicsymbols('q', 1)
|
||||
assert isinstance(A[0], CoordinateSym) and \
|
||||
isinstance(A[0], CoordinateSym) and \
|
||||
isinstance(A[0], CoordinateSym)
|
||||
assert A.variable_map(A) == {A[0]:A[0], A[1]:A[1], A[2]:A[2]}
|
||||
assert A[0].frame == A
|
||||
B = A.orientnew('B', 'Axis', [q, A.z])
|
||||
assert B.variable_map(A) == {B[2]: A[2], B[1]: -A[0]*sin(q) + A[1]*cos(q),
|
||||
B[0]: A[0]*cos(q) + A[1]*sin(q)}
|
||||
assert A.variable_map(B) == {A[0]: B[0]*cos(q) - B[1]*sin(q),
|
||||
A[1]: B[0]*sin(q) + B[1]*cos(q), A[2]: B[2]}
|
||||
assert time_derivative(B[0], A) == -A[0]*sin(q)*qd + A[1]*cos(q)*qd
|
||||
assert time_derivative(B[1], A) == -A[0]*cos(q)*qd - A[1]*sin(q)*qd
|
||||
assert time_derivative(B[2], A) == 0
|
||||
assert express(B[0], A, variables=True) == A[0]*cos(q) + A[1]*sin(q)
|
||||
assert express(B[1], A, variables=True) == -A[0]*sin(q) + A[1]*cos(q)
|
||||
assert express(B[2], A, variables=True) == A[2]
|
||||
assert time_derivative(A[0]*A.x + A[1]*A.y + A[2]*A.z, B) == A[1]*qd*A.x - A[0]*qd*A.y
|
||||
assert time_derivative(B[0]*B.x + B[1]*B.y + B[2]*B.z, A) == - B[1]*qd*B.x + B[0]*qd*B.y
|
||||
assert express(B[0]*B[1]*B[2], A, variables=True) == \
|
||||
A[2]*(-A[0]*sin(q) + A[1]*cos(q))*(A[0]*cos(q) + A[1]*sin(q))
|
||||
assert (time_derivative(B[0]*B[1]*B[2], A) -
|
||||
(A[2]*(-A[0]**2*cos(2*q) -
|
||||
2*A[0]*A[1]*sin(2*q) +
|
||||
A[1]**2*cos(2*q))*qd)).trigsimp() == 0
|
||||
assert express(B[0]*B.x + B[1]*B.y + B[2]*B.z, A) == \
|
||||
(B[0]*cos(q) - B[1]*sin(q))*A.x + (B[0]*sin(q) + \
|
||||
B[1]*cos(q))*A.y + B[2]*A.z
|
||||
assert express(B[0]*B.x + B[1]*B.y + B[2]*B.z, A,
|
||||
variables=True).simplify() == A[0]*A.x + A[1]*A.y + A[2]*A.z
|
||||
assert express(A[0]*A.x + A[1]*A.y + A[2]*A.z, B) == \
|
||||
(A[0]*cos(q) + A[1]*sin(q))*B.x + \
|
||||
(-A[0]*sin(q) + A[1]*cos(q))*B.y + A[2]*B.z
|
||||
assert express(A[0]*A.x + A[1]*A.y + A[2]*A.z, B,
|
||||
variables=True).simplify() == B[0]*B.x + B[1]*B.y + B[2]*B.z
|
||||
N = B.orientnew('N', 'Axis', [-q, B.z])
|
||||
assert ({k: v.simplify() for k, v in N.variable_map(A).items()} ==
|
||||
{N[0]: A[0], N[2]: A[2], N[1]: A[1]})
|
||||
C = A.orientnew('C', 'Axis', [q, A.x + A.y + A.z])
|
||||
mapping = A.variable_map(C)
|
||||
assert trigsimp(mapping[A[0]]) == (2*C[0]*cos(q)/3 + C[0]/3 -
|
||||
2*C[1]*sin(q + pi/6)/3 +
|
||||
C[1]/3 - 2*C[2]*cos(q + pi/3)/3 +
|
||||
C[2]/3)
|
||||
assert trigsimp(mapping[A[1]]) == -2*C[0]*cos(q + pi/3)/3 + \
|
||||
C[0]/3 + 2*C[1]*cos(q)/3 + C[1]/3 - 2*C[2]*sin(q + pi/6)/3 + C[2]/3
|
||||
assert trigsimp(mapping[A[2]]) == -2*C[0]*sin(q + pi/6)/3 + C[0]/3 - \
|
||||
2*C[1]*cos(q + pi/3)/3 + C[1]/3 + 2*C[2]*cos(q)/3 + C[2]/3
|
||||
|
||||
|
||||
def test_ang_vel():
|
||||
q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4')
|
||||
q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1)
|
||||
N = ReferenceFrame('N')
|
||||
A = N.orientnew('A', 'Axis', [q1, N.z])
|
||||
B = A.orientnew('B', 'Axis', [q2, A.x])
|
||||
C = B.orientnew('C', 'Axis', [q3, B.y])
|
||||
D = N.orientnew('D', 'Axis', [q4, N.y])
|
||||
u1, u2, u3 = dynamicsymbols('u1 u2 u3')
|
||||
assert A.ang_vel_in(N) == (q1d)*A.z
|
||||
assert B.ang_vel_in(N) == (q2d)*B.x + (q1d)*A.z
|
||||
assert C.ang_vel_in(N) == (q3d)*C.y + (q2d)*B.x + (q1d)*A.z
|
||||
|
||||
A2 = N.orientnew('A2', 'Axis', [q4, N.y])
|
||||
assert N.ang_vel_in(N) == 0
|
||||
assert N.ang_vel_in(A) == -q1d*N.z
|
||||
assert N.ang_vel_in(B) == -q1d*A.z - q2d*B.x
|
||||
assert N.ang_vel_in(C) == -q1d*A.z - q2d*B.x - q3d*B.y
|
||||
assert N.ang_vel_in(A2) == -q4d*N.y
|
||||
|
||||
assert A.ang_vel_in(N) == q1d*N.z
|
||||
assert A.ang_vel_in(A) == 0
|
||||
assert A.ang_vel_in(B) == - q2d*B.x
|
||||
assert A.ang_vel_in(C) == - q2d*B.x - q3d*B.y
|
||||
assert A.ang_vel_in(A2) == q1d*N.z - q4d*N.y
|
||||
|
||||
assert B.ang_vel_in(N) == q1d*A.z + q2d*A.x
|
||||
assert B.ang_vel_in(A) == q2d*A.x
|
||||
assert B.ang_vel_in(B) == 0
|
||||
assert B.ang_vel_in(C) == -q3d*B.y
|
||||
assert B.ang_vel_in(A2) == q1d*A.z + q2d*A.x - q4d*N.y
|
||||
|
||||
assert C.ang_vel_in(N) == q1d*A.z + q2d*A.x + q3d*B.y
|
||||
assert C.ang_vel_in(A) == q2d*A.x + q3d*C.y
|
||||
assert C.ang_vel_in(B) == q3d*B.y
|
||||
assert C.ang_vel_in(C) == 0
|
||||
assert C.ang_vel_in(A2) == q1d*A.z + q2d*A.x + q3d*B.y - q4d*N.y
|
||||
|
||||
assert A2.ang_vel_in(N) == q4d*A2.y
|
||||
assert A2.ang_vel_in(A) == q4d*A2.y - q1d*N.z
|
||||
assert A2.ang_vel_in(B) == q4d*N.y - q1d*A.z - q2d*A.x
|
||||
assert A2.ang_vel_in(C) == q4d*N.y - q1d*A.z - q2d*A.x - q3d*B.y
|
||||
assert A2.ang_vel_in(A2) == 0
|
||||
|
||||
C.set_ang_vel(N, u1*C.x + u2*C.y + u3*C.z)
|
||||
assert C.ang_vel_in(N) == (u1)*C.x + (u2)*C.y + (u3)*C.z
|
||||
assert N.ang_vel_in(C) == (-u1)*C.x + (-u2)*C.y + (-u3)*C.z
|
||||
assert C.ang_vel_in(D) == (u1)*C.x + (u2)*C.y + (u3)*C.z + (-q4d)*D.y
|
||||
assert D.ang_vel_in(C) == (-u1)*C.x + (-u2)*C.y + (-u3)*C.z + (q4d)*D.y
|
||||
|
||||
q0 = dynamicsymbols('q0')
|
||||
q0d = dynamicsymbols('q0', 1)
|
||||
E = N.orientnew('E', 'Quaternion', (q0, q1, q2, q3))
|
||||
assert E.ang_vel_in(N) == (
|
||||
2 * (q1d * q0 + q2d * q3 - q3d * q2 - q0d * q1) * E.x +
|
||||
2 * (q2d * q0 + q3d * q1 - q1d * q3 - q0d * q2) * E.y +
|
||||
2 * (q3d * q0 + q1d * q2 - q2d * q1 - q0d * q3) * E.z)
|
||||
|
||||
F = N.orientnew('F', 'Body', (q1, q2, q3), 313)
|
||||
assert F.ang_vel_in(N) == ((sin(q2)*sin(q3)*q1d + cos(q3)*q2d)*F.x +
|
||||
(sin(q2)*cos(q3)*q1d - sin(q3)*q2d)*F.y + (cos(q2)*q1d + q3d)*F.z)
|
||||
G = N.orientnew('G', 'Axis', (q1, N.x + N.y))
|
||||
assert G.ang_vel_in(N) == q1d * (N.x + N.y).normalize()
|
||||
assert N.ang_vel_in(G) == -q1d * (N.x + N.y).normalize()
|
||||
|
||||
|
||||
def test_dcm():
|
||||
q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4')
|
||||
N = ReferenceFrame('N')
|
||||
A = N.orientnew('A', 'Axis', [q1, N.z])
|
||||
B = A.orientnew('B', 'Axis', [q2, A.x])
|
||||
C = B.orientnew('C', 'Axis', [q3, B.y])
|
||||
D = N.orientnew('D', 'Axis', [q4, N.y])
|
||||
E = N.orientnew('E', 'Space', [q1, q2, q3], '123')
|
||||
assert N.dcm(C) == Matrix([
|
||||
[- sin(q1) * sin(q2) * sin(q3) + cos(q1) * cos(q3), - sin(q1) *
|
||||
cos(q2), sin(q1) * sin(q2) * cos(q3) + sin(q3) * cos(q1)], [sin(q1) *
|
||||
cos(q3) + sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2), sin(q1) *
|
||||
sin(q3) - sin(q2) * cos(q1) * cos(q3)], [- sin(q3) * cos(q2), sin(q2),
|
||||
cos(q2) * cos(q3)]])
|
||||
# This is a little touchy. Is it ok to use simplify in assert?
|
||||
test_mat = D.dcm(C) - Matrix(
|
||||
[[cos(q1) * cos(q3) * cos(q4) - sin(q3) * (- sin(q4) * cos(q2) +
|
||||
sin(q1) * sin(q2) * cos(q4)), - sin(q2) * sin(q4) - sin(q1) *
|
||||
cos(q2) * cos(q4), sin(q3) * cos(q1) * cos(q4) + cos(q3) * (- sin(q4) *
|
||||
cos(q2) + sin(q1) * sin(q2) * cos(q4))], [sin(q1) * cos(q3) +
|
||||
sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2), sin(q1) * sin(q3) -
|
||||
sin(q2) * cos(q1) * cos(q3)], [sin(q4) * cos(q1) * cos(q3) -
|
||||
sin(q3) * (cos(q2) * cos(q4) + sin(q1) * sin(q2) * sin(q4)), sin(q2) *
|
||||
cos(q4) - sin(q1) * sin(q4) * cos(q2), sin(q3) * sin(q4) * cos(q1) +
|
||||
cos(q3) * (cos(q2) * cos(q4) + sin(q1) * sin(q2) * sin(q4))]])
|
||||
assert test_mat.expand() == zeros(3, 3)
|
||||
assert E.dcm(N) == Matrix(
|
||||
[[cos(q2)*cos(q3), sin(q3)*cos(q2), -sin(q2)],
|
||||
[sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), sin(q1)*sin(q2)*sin(q3) +
|
||||
cos(q1)*cos(q3), sin(q1)*cos(q2)], [sin(q1)*sin(q3) +
|
||||
sin(q2)*cos(q1)*cos(q3), - sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1),
|
||||
cos(q1)*cos(q2)]])
|
||||
|
||||
def test_w_diff_dcm1():
|
||||
# Ref:
|
||||
# Dynamics Theory and Applications, Kane 1985
|
||||
# Sec. 2.1 ANGULAR VELOCITY
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
|
||||
c11, c12, c13 = dynamicsymbols('C11 C12 C13')
|
||||
c21, c22, c23 = dynamicsymbols('C21 C22 C23')
|
||||
c31, c32, c33 = dynamicsymbols('C31 C32 C33')
|
||||
|
||||
c11d, c12d, c13d = dynamicsymbols('C11 C12 C13', level=1)
|
||||
c21d, c22d, c23d = dynamicsymbols('C21 C22 C23', level=1)
|
||||
c31d, c32d, c33d = dynamicsymbols('C31 C32 C33', level=1)
|
||||
|
||||
DCM = Matrix([
|
||||
[c11, c12, c13],
|
||||
[c21, c22, c23],
|
||||
[c31, c32, c33]
|
||||
])
|
||||
|
||||
B.orient(A, 'DCM', DCM)
|
||||
b1a = (B.x).express(A)
|
||||
b2a = (B.y).express(A)
|
||||
b3a = (B.z).express(A)
|
||||
|
||||
# Equation (2.1.1)
|
||||
B.set_ang_vel(A, B.x*(dot((b3a).dt(A), B.y))
|
||||
+ B.y*(dot((b1a).dt(A), B.z))
|
||||
+ B.z*(dot((b2a).dt(A), B.x)))
|
||||
|
||||
# Equation (2.1.21)
|
||||
expr = ( (c12*c13d + c22*c23d + c32*c33d)*B.x
|
||||
+ (c13*c11d + c23*c21d + c33*c31d)*B.y
|
||||
+ (c11*c12d + c21*c22d + c31*c32d)*B.z)
|
||||
assert B.ang_vel_in(A) - expr == 0
|
||||
|
||||
def test_w_diff_dcm2():
|
||||
q1, q2, q3 = dynamicsymbols('q1:4')
|
||||
N = ReferenceFrame('N')
|
||||
A = N.orientnew('A', 'axis', [q1, N.x])
|
||||
B = A.orientnew('B', 'axis', [q2, A.y])
|
||||
C = B.orientnew('C', 'axis', [q3, B.z])
|
||||
|
||||
DCM = C.dcm(N).T
|
||||
D = N.orientnew('D', 'DCM', DCM)
|
||||
|
||||
# Frames D and C are the same ReferenceFrame,
|
||||
# since they have equal DCM respect to frame N.
|
||||
# Therefore, D and C should have same angle velocity in N.
|
||||
assert D.dcm(N) == C.dcm(N) == Matrix([
|
||||
[cos(q2)*cos(q3), sin(q1)*sin(q2)*cos(q3) +
|
||||
sin(q3)*cos(q1), sin(q1)*sin(q3) -
|
||||
sin(q2)*cos(q1)*cos(q3)], [-sin(q3)*cos(q2),
|
||||
-sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3),
|
||||
sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)],
|
||||
[sin(q2), -sin(q1)*cos(q2), cos(q1)*cos(q2)]])
|
||||
assert (D.ang_vel_in(N) - C.ang_vel_in(N)).express(N).simplify() == 0
|
||||
|
||||
def test_orientnew_respects_parent_class():
|
||||
class MyReferenceFrame(ReferenceFrame):
|
||||
pass
|
||||
B = MyReferenceFrame('B')
|
||||
C = B.orientnew('C', 'Axis', [0, B.x])
|
||||
assert isinstance(C, MyReferenceFrame)
|
||||
|
||||
|
||||
def test_orientnew_respects_input_indices():
|
||||
N = ReferenceFrame('N')
|
||||
q1 = dynamicsymbols('q1')
|
||||
A = N.orientnew('a', 'Axis', [q1, N.z])
|
||||
#modify default indices:
|
||||
minds = [x+'1' for x in N.indices]
|
||||
B = N.orientnew('b', 'Axis', [q1, N.z], indices=minds)
|
||||
|
||||
assert N.indices == A.indices
|
||||
assert B.indices == minds
|
||||
|
||||
def test_orientnew_respects_input_latexs():
|
||||
N = ReferenceFrame('N')
|
||||
q1 = dynamicsymbols('q1')
|
||||
A = N.orientnew('a', 'Axis', [q1, N.z])
|
||||
|
||||
#build default and alternate latex_vecs:
|
||||
def_latex_vecs = [(r"\mathbf{\hat{%s}_%s}" % (A.name.lower(),
|
||||
A.indices[0])), (r"\mathbf{\hat{%s}_%s}" %
|
||||
(A.name.lower(), A.indices[1])),
|
||||
(r"\mathbf{\hat{%s}_%s}" % (A.name.lower(),
|
||||
A.indices[2]))]
|
||||
|
||||
name = 'b'
|
||||
indices = [x+'1' for x in N.indices]
|
||||
new_latex_vecs = [(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
|
||||
indices[0])), (r"\mathbf{\hat{%s}_{%s}}" %
|
||||
(name.lower(), indices[1])),
|
||||
(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
|
||||
indices[2]))]
|
||||
|
||||
B = N.orientnew(name, 'Axis', [q1, N.z], latexs=new_latex_vecs)
|
||||
|
||||
assert A.latex_vecs == def_latex_vecs
|
||||
assert B.latex_vecs == new_latex_vecs
|
||||
assert B.indices != indices
|
||||
|
||||
def test_orientnew_respects_input_variables():
|
||||
N = ReferenceFrame('N')
|
||||
q1 = dynamicsymbols('q1')
|
||||
A = N.orientnew('a', 'Axis', [q1, N.z])
|
||||
|
||||
#build non-standard variable names
|
||||
name = 'b'
|
||||
new_variables = ['notb_'+x+'1' for x in N.indices]
|
||||
B = N.orientnew(name, 'Axis', [q1, N.z], variables=new_variables)
|
||||
|
||||
for j,var in enumerate(A.varlist):
|
||||
assert var.name == A.name + '_' + A.indices[j]
|
||||
|
||||
for j,var in enumerate(B.varlist):
|
||||
assert var.name == new_variables[j]
|
||||
|
||||
def test_issue_10348():
|
||||
u = dynamicsymbols('u:3')
|
||||
I = ReferenceFrame('I')
|
||||
I.orientnew('A', 'space', u, 'XYZ')
|
||||
|
||||
|
||||
def test_issue_11503():
|
||||
A = ReferenceFrame("A")
|
||||
A.orientnew("B", "Axis", [35, A.y])
|
||||
C = ReferenceFrame("C")
|
||||
A.orient(C, "Axis", [70, C.z])
|
||||
|
||||
|
||||
def test_partial_velocity():
|
||||
|
||||
N = ReferenceFrame('N')
|
||||
A = ReferenceFrame('A')
|
||||
|
||||
u1, u2 = dynamicsymbols('u1, u2')
|
||||
|
||||
A.set_ang_vel(N, u1 * A.x + u2 * N.y)
|
||||
|
||||
assert N.partial_velocity(A, u1) == -A.x
|
||||
assert N.partial_velocity(A, u1, u2) == (-A.x, -N.y)
|
||||
|
||||
assert A.partial_velocity(N, u1) == A.x
|
||||
assert A.partial_velocity(N, u1, u2) == (A.x, N.y)
|
||||
|
||||
assert N.partial_velocity(N, u1) == 0
|
||||
assert A.partial_velocity(A, u1) == 0
|
||||
|
||||
|
||||
def test_issue_11498():
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
|
||||
# Identity transformation
|
||||
A.orient(B, 'DCM', eye(3))
|
||||
assert A.dcm(B) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
||||
assert B.dcm(A) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
||||
|
||||
# x -> y
|
||||
# y -> -z
|
||||
# z -> -x
|
||||
A.orient(B, 'DCM', Matrix([[0, 1, 0], [0, 0, -1], [-1, 0, 0]]))
|
||||
assert B.dcm(A) == Matrix([[0, 1, 0], [0, 0, -1], [-1, 0, 0]])
|
||||
assert A.dcm(B) == Matrix([[0, 0, -1], [1, 0, 0], [0, -1, 0]])
|
||||
assert B.dcm(A).T == A.dcm(B)
|
||||
|
||||
|
||||
def test_reference_frame():
|
||||
raises(TypeError, lambda: ReferenceFrame(0))
|
||||
raises(TypeError, lambda: ReferenceFrame('N', 0))
|
||||
raises(ValueError, lambda: ReferenceFrame('N', [0, 1]))
|
||||
raises(TypeError, lambda: ReferenceFrame('N', [0, 1, 2]))
|
||||
raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], 0))
|
||||
raises(ValueError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], [0, 1]))
|
||||
raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], [0, 1, 2]))
|
||||
raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'],
|
||||
['a', 'b', 'c'], 0))
|
||||
raises(ValueError, lambda: ReferenceFrame('N', ['a', 'b', 'c'],
|
||||
['a', 'b', 'c'], [0, 1]))
|
||||
raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'],
|
||||
['a', 'b', 'c'], [0, 1, 2]))
|
||||
N = ReferenceFrame('N')
|
||||
assert N[0] == CoordinateSym('N_x', N, 0)
|
||||
assert N[1] == CoordinateSym('N_y', N, 1)
|
||||
assert N[2] == CoordinateSym('N_z', N, 2)
|
||||
raises(ValueError, lambda: N[3])
|
||||
N = ReferenceFrame('N', ['a', 'b', 'c'])
|
||||
assert N['a'] == N.x
|
||||
assert N['b'] == N.y
|
||||
assert N['c'] == N.z
|
||||
raises(ValueError, lambda: N['d'])
|
||||
assert str(N) == 'N'
|
||||
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
|
||||
raises(TypeError, lambda: A.orient(B, 'DCM', 0))
|
||||
raises(TypeError, lambda: B.orient(N, 'Space', [q1, q2, q3], '222'))
|
||||
raises(TypeError, lambda: B.orient(N, 'Axis', [q1, N.x + 2 * N.y], '222'))
|
||||
raises(TypeError, lambda: B.orient(N, 'Axis', q1))
|
||||
raises(IndexError, lambda: B.orient(N, 'Axis', [q1]))
|
||||
raises(TypeError, lambda: B.orient(N, 'Quaternion', [q0, q1, q2, q3], '222'))
|
||||
raises(TypeError, lambda: B.orient(N, 'Quaternion', q0))
|
||||
raises(TypeError, lambda: B.orient(N, 'Quaternion', [q0, q1, q2]))
|
||||
raises(NotImplementedError, lambda: B.orient(N, 'Foo', [q0, q1, q2]))
|
||||
raises(TypeError, lambda: B.orient(N, 'Body', [q1, q2], '232'))
|
||||
raises(TypeError, lambda: B.orient(N, 'Space', [q1, q2], '232'))
|
||||
|
||||
N.set_ang_acc(B, 0)
|
||||
assert N.ang_acc_in(B) == Vector(0)
|
||||
N.set_ang_vel(B, 0)
|
||||
assert N.ang_vel_in(B) == Vector(0)
|
||||
|
||||
|
||||
def test_check_frame():
|
||||
raises(VectorTypeError, lambda: _check_frame(0))
|
||||
|
||||
|
||||
def test_dcm_diff_16824():
|
||||
# NOTE : This is a regression test for the bug introduced in PR 14758,
|
||||
# identified in 16824, and solved by PR 16828.
|
||||
|
||||
# This is the solution to Problem 2.2 on page 264 in Kane & Lenvinson's
|
||||
# 1985 book.
|
||||
|
||||
q1, q2, q3 = dynamicsymbols('q1:4')
|
||||
|
||||
s1 = sin(q1)
|
||||
c1 = cos(q1)
|
||||
s2 = sin(q2)
|
||||
c2 = cos(q2)
|
||||
s3 = sin(q3)
|
||||
c3 = cos(q3)
|
||||
|
||||
dcm = Matrix([[c2*c3, s1*s2*c3 - s3*c1, c1*s2*c3 + s3*s1],
|
||||
[c2*s3, s1*s2*s3 + c3*c1, c1*s2*s3 - c3*s1],
|
||||
[-s2, s1*c2, c1*c2]])
|
||||
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
B.orient(A, 'DCM', dcm)
|
||||
|
||||
AwB = B.ang_vel_in(A)
|
||||
|
||||
alpha2 = s3*c2*q1.diff() + c3*q2.diff()
|
||||
beta2 = s1*c2*q3.diff() + c1*q2.diff()
|
||||
|
||||
assert simplify(AwB.dot(A.y) - alpha2) == 0
|
||||
assert simplify(AwB.dot(B.y) - beta2) == 0
|
||||
|
||||
def test_orient_explicit():
|
||||
cxx, cyy, czz = dynamicsymbols('c_{xx}, c_{yy}, c_{zz}')
|
||||
cxy, cxz, cyx = dynamicsymbols('c_{xy}, c_{xz}, c_{yx}')
|
||||
cyz, czx, czy = dynamicsymbols('c_{yz}, c_{zx}, c_{zy}')
|
||||
dcxx, dcyy, dczz = dynamicsymbols('c_{xx}, c_{yy}, c_{zz}', 1)
|
||||
dcxy, dcxz, dcyx = dynamicsymbols('c_{xy}, c_{xz}, c_{yx}', 1)
|
||||
dcyz, dczx, dczy = dynamicsymbols('c_{yz}, c_{zx}, c_{zy}', 1)
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
B_C_A = Matrix([[cxx, cxy, cxz],
|
||||
[cyx, cyy, cyz],
|
||||
[czx, czy, czz]])
|
||||
B_w_A = ((cyx*dczx + cyy*dczy + cyz*dczz)*B.x +
|
||||
(czx*dcxx + czy*dcxy + czz*dcxz)*B.y +
|
||||
(cxx*dcyx + cxy*dcyy + cxz*dcyz)*B.z)
|
||||
A.orient_explicit(B, B_C_A)
|
||||
assert B.dcm(A) == B_C_A
|
||||
assert A.ang_vel_in(B) == B_w_A
|
||||
assert B.ang_vel_in(A) == -B_w_A
|
||||
|
||||
def test_orient_dcm():
|
||||
cxx, cyy, czz = dynamicsymbols('c_{xx}, c_{yy}, c_{zz}')
|
||||
cxy, cxz, cyx = dynamicsymbols('c_{xy}, c_{xz}, c_{yx}')
|
||||
cyz, czx, czy = dynamicsymbols('c_{yz}, c_{zx}, c_{zy}')
|
||||
B_C_A = Matrix([[cxx, cxy, cxz],
|
||||
[cyx, cyy, cyz],
|
||||
[czx, czy, czz]])
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
B.orient_dcm(A, B_C_A)
|
||||
assert B.dcm(A) == Matrix([[cxx, cxy, cxz],
|
||||
[cyx, cyy, cyz],
|
||||
[czx, czy, czz]])
|
||||
|
||||
def test_orient_axis():
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
A.orient_axis(B,-B.x, 1)
|
||||
A1 = A.dcm(B)
|
||||
A.orient_axis(B, B.x, -1)
|
||||
A2 = A.dcm(B)
|
||||
A.orient_axis(B, 1, -B.x)
|
||||
A3 = A.dcm(B)
|
||||
assert A1 == A2
|
||||
assert A2 == A3
|
||||
raises(TypeError, lambda: A.orient_axis(B, 1, 1))
|
||||
|
||||
def test_orient_body():
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
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_advanced():
|
||||
q1, q2, q3 = dynamicsymbols('q1:4')
|
||||
c1, c2, c3 = symbols('c1:4')
|
||||
u1, u2, u3 = dynamicsymbols('q1:4', 1)
|
||||
|
||||
# Test with everything as dynamicsymbols
|
||||
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
||||
B.orient_body_fixed(A, (q1, q2, q3), 'zxy')
|
||||
assert A.dcm(B) == Matrix([
|
||||
[-sin(q1) * sin(q2) * sin(q3) + cos(q1) * cos(q3), -sin(q1) * cos(q2),
|
||||
sin(q1) * sin(q2) * cos(q3) + sin(q3) * cos(q1)],
|
||||
[sin(q1) * cos(q3) + sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2),
|
||||
sin(q1) * sin(q3) - sin(q2) * cos(q1) * cos(q3)],
|
||||
[-sin(q3) * cos(q2), sin(q2), cos(q2) * cos(q3)]])
|
||||
assert B.ang_vel_in(A).to_matrix(B) == Matrix([
|
||||
[-sin(q3) * cos(q2) * u1 + cos(q3) * u2],
|
||||
[sin(q2) * u1 + u3],
|
||||
[sin(q3) * u2 + cos(q2) * cos(q3) * u1]])
|
||||
|
||||
# Test with constant symbol
|
||||
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
||||
B.orient_body_fixed(A, (q1, c2, q3), 131)
|
||||
assert A.dcm(B) == Matrix([
|
||||
[cos(c2), -sin(c2) * cos(q3), sin(c2) * sin(q3)],
|
||||
[sin(c2) * cos(q1), -sin(q1) * sin(q3) + cos(c2) * cos(q1) * cos(q3),
|
||||
-sin(q1) * cos(q3) - sin(q3) * cos(c2) * cos(q1)],
|
||||
[sin(c2) * sin(q1), sin(q1) * cos(c2) * cos(q3) + sin(q3) * cos(q1),
|
||||
-sin(q1) * sin(q3) * cos(c2) + cos(q1) * cos(q3)]])
|
||||
assert B.ang_vel_in(A).to_matrix(B) == Matrix([
|
||||
[cos(c2) * u1 + u3],
|
||||
[-sin(c2) * cos(q3) * u1],
|
||||
[sin(c2) * sin(q3) * u1]])
|
||||
|
||||
# Test all symbols not time dependent
|
||||
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
||||
B.orient_body_fixed(A, (c1, c2, c3), 123)
|
||||
assert B.ang_vel_in(A) == Vector(0)
|
||||
|
||||
|
||||
def test_orient_space_advanced():
|
||||
# space fixed is in the end like body fixed only in opposite order
|
||||
q1, q2, q3 = dynamicsymbols('q1:4')
|
||||
c1, c2, c3 = symbols('c1:4')
|
||||
u1, u2, u3 = dynamicsymbols('q1:4', 1)
|
||||
|
||||
# Test with everything as dynamicsymbols
|
||||
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
||||
B.orient_space_fixed(A, (q3, q2, q1), 'yxz')
|
||||
assert A.dcm(B) == Matrix([
|
||||
[-sin(q1) * sin(q2) * sin(q3) + cos(q1) * cos(q3), -sin(q1) * cos(q2),
|
||||
sin(q1) * sin(q2) * cos(q3) + sin(q3) * cos(q1)],
|
||||
[sin(q1) * cos(q3) + sin(q2) * sin(q3) * cos(q1), cos(q1) * cos(q2),
|
||||
sin(q1) * sin(q3) - sin(q2) * cos(q1) * cos(q3)],
|
||||
[-sin(q3) * cos(q2), sin(q2), cos(q2) * cos(q3)]])
|
||||
assert B.ang_vel_in(A).to_matrix(B) == Matrix([
|
||||
[-sin(q3) * cos(q2) * u1 + cos(q3) * u2],
|
||||
[sin(q2) * u1 + u3],
|
||||
[sin(q3) * u2 + cos(q2) * cos(q3) * u1]])
|
||||
|
||||
# Test with constant symbol
|
||||
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
||||
B.orient_space_fixed(A, (q3, c2, q1), 131)
|
||||
assert A.dcm(B) == Matrix([
|
||||
[cos(c2), -sin(c2) * cos(q3), sin(c2) * sin(q3)],
|
||||
[sin(c2) * cos(q1), -sin(q1) * sin(q3) + cos(c2) * cos(q1) * cos(q3),
|
||||
-sin(q1) * cos(q3) - sin(q3) * cos(c2) * cos(q1)],
|
||||
[sin(c2) * sin(q1), sin(q1) * cos(c2) * cos(q3) + sin(q3) * cos(q1),
|
||||
-sin(q1) * sin(q3) * cos(c2) + cos(q1) * cos(q3)]])
|
||||
assert B.ang_vel_in(A).to_matrix(B) == Matrix([
|
||||
[cos(c2) * u1 + u3],
|
||||
[-sin(c2) * cos(q3) * u1],
|
||||
[sin(c2) * sin(q3) * u1]])
|
||||
|
||||
# Test all symbols not time dependent
|
||||
A, B = ReferenceFrame('A'), ReferenceFrame('B')
|
||||
B.orient_space_fixed(A, (c1, c2, c3), 123)
|
||||
assert B.ang_vel_in(A) == Vector(0)
|
||||
|
||||
|
||||
def test_orient_body_simple_ang_vel():
|
||||
"""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')
|
||||
B.orient_space_fixed(A, (0,0,0), '123')
|
||||
assert B.dcm(A) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
||||
|
||||
def test_orient_quaternion():
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
B.orient_quaternion(A, (0,0,0,0))
|
||||
assert B.dcm(A) == Matrix([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
|
||||
|
||||
def test_looped_frame_warning():
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
C = ReferenceFrame('C')
|
||||
|
||||
a, b, c = symbols('a b c')
|
||||
B.orient_axis(A, A.x, a)
|
||||
C.orient_axis(B, B.x, b)
|
||||
|
||||
with warnings.catch_warnings(record = True) as w:
|
||||
warnings.simplefilter("always")
|
||||
A.orient_axis(C, C.x, c)
|
||||
assert issubclass(w[-1].category, UserWarning)
|
||||
assert 'Loops are defined among the orientation of frames. ' + \
|
||||
'This is likely not desired and may cause errors in your calculations.' in str(w[-1].message)
|
||||
|
||||
def test_frame_dict():
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
C = ReferenceFrame('C')
|
||||
|
||||
a, b, c = symbols('a b c')
|
||||
|
||||
B.orient_axis(A, A.x, a)
|
||||
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(a), -sin(a)],[0, sin(a), cos(a)]])}
|
||||
assert B._dcm_dict == {A: Matrix([[1, 0, 0],[0, cos(a), sin(a)],[0, -sin(a), cos(a)]])}
|
||||
assert C._dcm_dict == {}
|
||||
|
||||
B.orient_axis(C, C.x, b)
|
||||
# Previous relation is not wiped
|
||||
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(a), -sin(a)],[0, sin(a), cos(a)]])}
|
||||
assert B._dcm_dict == {A: Matrix([[1, 0, 0],[0, cos(a), sin(a)],[0, -sin(a), cos(a)]]), \
|
||||
C: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]])}
|
||||
assert C._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])}
|
||||
|
||||
A.orient_axis(B, B.x, c)
|
||||
# Previous relation is updated
|
||||
assert B._dcm_dict == {C: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]]),\
|
||||
A: Matrix([[1, 0, 0],[0, cos(c), -sin(c)],[0, sin(c), cos(c)]])}
|
||||
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(c), sin(c)],[0, -sin(c), cos(c)]])}
|
||||
assert C._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])}
|
||||
|
||||
def test_dcm_cache_dict():
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
C = ReferenceFrame('C')
|
||||
D = ReferenceFrame('D')
|
||||
|
||||
a, b, c = symbols('a b c')
|
||||
|
||||
B.orient_axis(A, A.x, a)
|
||||
C.orient_axis(B, B.x, b)
|
||||
D.orient_axis(C, C.x, c)
|
||||
|
||||
assert D._dcm_dict == {C: Matrix([[1, 0, 0],[0, cos(c), sin(c)],[0, -sin(c), cos(c)]])}
|
||||
assert C._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]]), \
|
||||
D: Matrix([[1, 0, 0],[0, cos(c), -sin(c)],[0, sin(c), cos(c)]])}
|
||||
assert B._dcm_dict == {A: Matrix([[1, 0, 0],[0, cos(a), sin(a)],[0, -sin(a), cos(a)]]), \
|
||||
C: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])}
|
||||
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(a), -sin(a)],[0, sin(a), cos(a)]])}
|
||||
|
||||
assert D._dcm_dict == D._dcm_cache
|
||||
|
||||
D.dcm(A) # Check calculated dcm relation is stored in _dcm_cache and not in _dcm_dict
|
||||
assert list(A._dcm_cache.keys()) == [A, B, D]
|
||||
assert list(D._dcm_cache.keys()) == [C, A]
|
||||
assert list(A._dcm_dict.keys()) == [B]
|
||||
assert list(D._dcm_dict.keys()) == [C]
|
||||
assert A._dcm_dict != A._dcm_cache
|
||||
|
||||
A.orient_axis(B, B.x, b) # _dcm_cache of A is wiped out and new relation is stored.
|
||||
assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]])}
|
||||
assert A._dcm_dict == A._dcm_cache
|
||||
assert B._dcm_dict == {C: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]]), \
|
||||
A: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])}
|
||||
|
||||
def test_xx_dyad():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.xx == Vector.outer(N.x, N.x)
|
||||
assert F.xx == Vector.outer(F.x, F.x)
|
||||
|
||||
def test_xy_dyad():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.xy == Vector.outer(N.x, N.y)
|
||||
assert F.xy == Vector.outer(F.x, F.y)
|
||||
|
||||
def test_xz_dyad():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.xz == Vector.outer(N.x, N.z)
|
||||
assert F.xz == Vector.outer(F.x, F.z)
|
||||
|
||||
def test_yx_dyad():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.yx == Vector.outer(N.y, N.x)
|
||||
assert F.yx == Vector.outer(F.y, F.x)
|
||||
|
||||
def test_yy_dyad():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.yy == Vector.outer(N.y, N.y)
|
||||
assert F.yy == Vector.outer(F.y, F.y)
|
||||
|
||||
def test_yz_dyad():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.yz == Vector.outer(N.y, N.z)
|
||||
assert F.yz == Vector.outer(F.y, F.z)
|
||||
|
||||
def test_zx_dyad():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.zx == Vector.outer(N.z, N.x)
|
||||
assert F.zx == Vector.outer(F.z, F.x)
|
||||
|
||||
def test_zy_dyad():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.zy == Vector.outer(N.z, N.y)
|
||||
assert F.zy == Vector.outer(F.z, F.y)
|
||||
|
||||
def test_zz_dyad():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.zz == Vector.outer(N.z, N.z)
|
||||
assert F.zz == Vector.outer(F.z, F.z)
|
||||
|
||||
def test_unit_dyadic():
|
||||
N = ReferenceFrame('N')
|
||||
F = ReferenceFrame('F', indices=['1', '2', '3'])
|
||||
assert N.u == N.xx + N.yy + N.zz
|
||||
assert F.u == F.xx + F.yy + F.zz
|
||||
|
||||
|
||||
def test_pickle_frame():
|
||||
N = ReferenceFrame('N')
|
||||
A = ReferenceFrame('A')
|
||||
A.orient_axis(N, N.x, 1)
|
||||
A_C_N = A.dcm(N)
|
||||
N1 = pickle.loads(pickle.dumps(N))
|
||||
A1 = tuple(N1._dcm_dict.keys())[0]
|
||||
assert A1.dcm(N1) == A_C_N
|
||||
@@ -0,0 +1,509 @@
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.physics.vector import Dyadic, Point, ReferenceFrame, Vector
|
||||
from sympy.physics.vector.functions import (cross, dot, express,
|
||||
time_derivative,
|
||||
kinematic_equations, outer,
|
||||
partial_velocity,
|
||||
get_motion_params, dynamicsymbols)
|
||||
from sympy.simplify import trigsimp
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
q1, q2, q3, q4, q5 = symbols('q1 q2 q3 q4 q5')
|
||||
N = ReferenceFrame('N')
|
||||
A = N.orientnew('A', 'Axis', [q1, N.z])
|
||||
B = A.orientnew('B', 'Axis', [q2, A.x])
|
||||
C = B.orientnew('C', 'Axis', [q3, B.y])
|
||||
|
||||
|
||||
def test_dot():
|
||||
assert dot(A.x, A.x) == 1
|
||||
assert dot(A.x, A.y) == 0
|
||||
assert dot(A.x, A.z) == 0
|
||||
|
||||
assert dot(A.y, A.x) == 0
|
||||
assert dot(A.y, A.y) == 1
|
||||
assert dot(A.y, A.z) == 0
|
||||
|
||||
assert dot(A.z, A.x) == 0
|
||||
assert dot(A.z, A.y) == 0
|
||||
assert dot(A.z, A.z) == 1
|
||||
|
||||
|
||||
def test_dot_different_frames():
|
||||
assert dot(N.x, A.x) == cos(q1)
|
||||
assert dot(N.x, A.y) == -sin(q1)
|
||||
assert dot(N.x, A.z) == 0
|
||||
assert dot(N.y, A.x) == sin(q1)
|
||||
assert dot(N.y, A.y) == cos(q1)
|
||||
assert dot(N.y, A.z) == 0
|
||||
assert dot(N.z, A.x) == 0
|
||||
assert dot(N.z, A.y) == 0
|
||||
assert dot(N.z, A.z) == 1
|
||||
|
||||
assert trigsimp(dot(N.x, A.x + A.y)) == sqrt(2)*cos(q1 + pi/4)
|
||||
assert trigsimp(dot(N.x, A.x + A.y)) == trigsimp(dot(A.x + A.y, N.x))
|
||||
|
||||
assert dot(A.x, C.x) == cos(q3)
|
||||
assert dot(A.x, C.y) == 0
|
||||
assert dot(A.x, C.z) == sin(q3)
|
||||
assert dot(A.y, C.x) == sin(q2)*sin(q3)
|
||||
assert dot(A.y, C.y) == cos(q2)
|
||||
assert dot(A.y, C.z) == -sin(q2)*cos(q3)
|
||||
assert dot(A.z, C.x) == -cos(q2)*sin(q3)
|
||||
assert dot(A.z, C.y) == sin(q2)
|
||||
assert dot(A.z, C.z) == cos(q2)*cos(q3)
|
||||
|
||||
|
||||
def test_cross():
|
||||
assert cross(A.x, A.x) == 0
|
||||
assert cross(A.x, A.y) == A.z
|
||||
assert cross(A.x, A.z) == -A.y
|
||||
|
||||
assert cross(A.y, A.x) == -A.z
|
||||
assert cross(A.y, A.y) == 0
|
||||
assert cross(A.y, A.z) == A.x
|
||||
|
||||
assert cross(A.z, A.x) == A.y
|
||||
assert cross(A.z, A.y) == -A.x
|
||||
assert cross(A.z, A.z) == 0
|
||||
|
||||
|
||||
def test_cross_different_frames():
|
||||
assert cross(N.x, A.x) == sin(q1)*A.z
|
||||
assert cross(N.x, A.y) == cos(q1)*A.z
|
||||
assert cross(N.x, A.z) == -sin(q1)*A.x - cos(q1)*A.y
|
||||
assert cross(N.y, A.x) == -cos(q1)*A.z
|
||||
assert cross(N.y, A.y) == sin(q1)*A.z
|
||||
assert cross(N.y, A.z) == cos(q1)*A.x - sin(q1)*A.y
|
||||
assert cross(N.z, A.x) == A.y
|
||||
assert cross(N.z, A.y) == -A.x
|
||||
assert cross(N.z, A.z) == 0
|
||||
|
||||
assert cross(N.x, A.x) == sin(q1)*A.z
|
||||
assert cross(N.x, A.y) == cos(q1)*A.z
|
||||
assert cross(N.x, A.x + A.y) == sin(q1)*A.z + cos(q1)*A.z
|
||||
assert cross(A.x + A.y, N.x) == -sin(q1)*A.z - cos(q1)*A.z
|
||||
|
||||
assert cross(A.x, C.x) == sin(q3)*C.y
|
||||
assert cross(A.x, C.y) == -sin(q3)*C.x + cos(q3)*C.z
|
||||
assert cross(A.x, C.z) == -cos(q3)*C.y
|
||||
assert cross(C.x, A.x) == -sin(q3)*C.y
|
||||
assert cross(C.y, A.x).express(C).simplify() == sin(q3)*C.x - cos(q3)*C.z
|
||||
assert cross(C.z, A.x) == cos(q3)*C.y
|
||||
|
||||
def test_operator_match():
|
||||
"""Test that the output of dot, cross, outer functions match
|
||||
operator behavior.
|
||||
"""
|
||||
A = ReferenceFrame('A')
|
||||
v = A.x + A.y
|
||||
d = v | v
|
||||
zerov = Vector(0)
|
||||
zerod = Dyadic(0)
|
||||
|
||||
# dot products
|
||||
assert d & d == dot(d, d)
|
||||
assert d & zerod == dot(d, zerod)
|
||||
assert zerod & d == dot(zerod, d)
|
||||
assert d & v == dot(d, v)
|
||||
assert v & d == dot(v, d)
|
||||
assert d & zerov == dot(d, zerov)
|
||||
assert zerov & d == dot(zerov, d)
|
||||
raises(TypeError, lambda: dot(d, S.Zero))
|
||||
raises(TypeError, lambda: dot(S.Zero, d))
|
||||
raises(TypeError, lambda: dot(d, 0))
|
||||
raises(TypeError, lambda: dot(0, d))
|
||||
assert v & v == dot(v, v)
|
||||
assert v & zerov == dot(v, zerov)
|
||||
assert zerov & v == dot(zerov, v)
|
||||
raises(TypeError, lambda: dot(v, S.Zero))
|
||||
raises(TypeError, lambda: dot(S.Zero, v))
|
||||
raises(TypeError, lambda: dot(v, 0))
|
||||
raises(TypeError, lambda: dot(0, v))
|
||||
|
||||
# cross products
|
||||
raises(TypeError, lambda: cross(d, d))
|
||||
raises(TypeError, lambda: cross(d, zerod))
|
||||
raises(TypeError, lambda: cross(zerod, d))
|
||||
assert d ^ v == cross(d, v)
|
||||
assert v ^ d == cross(v, d)
|
||||
assert d ^ zerov == cross(d, zerov)
|
||||
assert zerov ^ d == cross(zerov, d)
|
||||
assert zerov ^ d == cross(zerov, d)
|
||||
raises(TypeError, lambda: cross(d, S.Zero))
|
||||
raises(TypeError, lambda: cross(S.Zero, d))
|
||||
raises(TypeError, lambda: cross(d, 0))
|
||||
raises(TypeError, lambda: cross(0, d))
|
||||
assert v ^ v == cross(v, v)
|
||||
assert v ^ zerov == cross(v, zerov)
|
||||
assert zerov ^ v == cross(zerov, v)
|
||||
raises(TypeError, lambda: cross(v, S.Zero))
|
||||
raises(TypeError, lambda: cross(S.Zero, v))
|
||||
raises(TypeError, lambda: cross(v, 0))
|
||||
raises(TypeError, lambda: cross(0, v))
|
||||
|
||||
# outer products
|
||||
raises(TypeError, lambda: outer(d, d))
|
||||
raises(TypeError, lambda: outer(d, zerod))
|
||||
raises(TypeError, lambda: outer(zerod, d))
|
||||
raises(TypeError, lambda: outer(d, v))
|
||||
raises(TypeError, lambda: outer(v, d))
|
||||
raises(TypeError, lambda: outer(d, zerov))
|
||||
raises(TypeError, lambda: outer(zerov, d))
|
||||
raises(TypeError, lambda: outer(zerov, d))
|
||||
raises(TypeError, lambda: outer(d, S.Zero))
|
||||
raises(TypeError, lambda: outer(S.Zero, d))
|
||||
raises(TypeError, lambda: outer(d, 0))
|
||||
raises(TypeError, lambda: outer(0, d))
|
||||
assert v | v == outer(v, v)
|
||||
assert v | zerov == outer(v, zerov)
|
||||
assert zerov | v == outer(zerov, v)
|
||||
raises(TypeError, lambda: outer(v, S.Zero))
|
||||
raises(TypeError, lambda: outer(S.Zero, v))
|
||||
raises(TypeError, lambda: outer(v, 0))
|
||||
raises(TypeError, lambda: outer(0, v))
|
||||
|
||||
|
||||
def test_express():
|
||||
assert express(Vector(0), N) == Vector(0)
|
||||
assert express(S.Zero, N) is S.Zero
|
||||
assert express(A.x, C) == cos(q3)*C.x + sin(q3)*C.z
|
||||
assert express(A.y, C) == sin(q2)*sin(q3)*C.x + cos(q2)*C.y - \
|
||||
sin(q2)*cos(q3)*C.z
|
||||
assert express(A.z, C) == -sin(q3)*cos(q2)*C.x + sin(q2)*C.y + \
|
||||
cos(q2)*cos(q3)*C.z
|
||||
assert express(A.x, N) == cos(q1)*N.x + sin(q1)*N.y
|
||||
assert express(A.y, N) == -sin(q1)*N.x + cos(q1)*N.y
|
||||
assert express(A.z, N) == N.z
|
||||
assert express(A.x, A) == A.x
|
||||
assert express(A.y, A) == A.y
|
||||
assert express(A.z, A) == A.z
|
||||
assert express(A.x, B) == B.x
|
||||
assert express(A.y, B) == cos(q2)*B.y - sin(q2)*B.z
|
||||
assert express(A.z, B) == sin(q2)*B.y + cos(q2)*B.z
|
||||
assert express(A.x, C) == cos(q3)*C.x + sin(q3)*C.z
|
||||
assert express(A.y, C) == sin(q2)*sin(q3)*C.x + cos(q2)*C.y - \
|
||||
sin(q2)*cos(q3)*C.z
|
||||
assert express(A.z, C) == -sin(q3)*cos(q2)*C.x + sin(q2)*C.y + \
|
||||
cos(q2)*cos(q3)*C.z
|
||||
# Check to make sure UnitVectors get converted properly
|
||||
assert express(N.x, N) == N.x
|
||||
assert express(N.y, N) == N.y
|
||||
assert express(N.z, N) == N.z
|
||||
assert express(N.x, A) == (cos(q1)*A.x - sin(q1)*A.y)
|
||||
assert express(N.y, A) == (sin(q1)*A.x + cos(q1)*A.y)
|
||||
assert express(N.z, A) == A.z
|
||||
assert express(N.x, B) == (cos(q1)*B.x - sin(q1)*cos(q2)*B.y +
|
||||
sin(q1)*sin(q2)*B.z)
|
||||
assert express(N.y, B) == (sin(q1)*B.x + cos(q1)*cos(q2)*B.y -
|
||||
sin(q2)*cos(q1)*B.z)
|
||||
assert express(N.z, B) == (sin(q2)*B.y + cos(q2)*B.z)
|
||||
assert express(N.x, C) == (
|
||||
(cos(q1)*cos(q3) - sin(q1)*sin(q2)*sin(q3))*C.x -
|
||||
sin(q1)*cos(q2)*C.y +
|
||||
(sin(q3)*cos(q1) + sin(q1)*sin(q2)*cos(q3))*C.z)
|
||||
assert express(N.y, C) == (
|
||||
(sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*C.x +
|
||||
cos(q1)*cos(q2)*C.y +
|
||||
(sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*C.z)
|
||||
assert express(N.z, C) == (-sin(q3)*cos(q2)*C.x + sin(q2)*C.y +
|
||||
cos(q2)*cos(q3)*C.z)
|
||||
|
||||
assert express(A.x, N) == (cos(q1)*N.x + sin(q1)*N.y)
|
||||
assert express(A.y, N) == (-sin(q1)*N.x + cos(q1)*N.y)
|
||||
assert express(A.z, N) == N.z
|
||||
assert express(A.x, A) == A.x
|
||||
assert express(A.y, A) == A.y
|
||||
assert express(A.z, A) == A.z
|
||||
assert express(A.x, B) == B.x
|
||||
assert express(A.y, B) == (cos(q2)*B.y - sin(q2)*B.z)
|
||||
assert express(A.z, B) == (sin(q2)*B.y + cos(q2)*B.z)
|
||||
assert express(A.x, C) == (cos(q3)*C.x + sin(q3)*C.z)
|
||||
assert express(A.y, C) == (sin(q2)*sin(q3)*C.x + cos(q2)*C.y -
|
||||
sin(q2)*cos(q3)*C.z)
|
||||
assert express(A.z, C) == (-sin(q3)*cos(q2)*C.x + sin(q2)*C.y +
|
||||
cos(q2)*cos(q3)*C.z)
|
||||
|
||||
assert express(B.x, N) == (cos(q1)*N.x + sin(q1)*N.y)
|
||||
assert express(B.y, N) == (-sin(q1)*cos(q2)*N.x +
|
||||
cos(q1)*cos(q2)*N.y + sin(q2)*N.z)
|
||||
assert express(B.z, N) == (sin(q1)*sin(q2)*N.x -
|
||||
sin(q2)*cos(q1)*N.y + cos(q2)*N.z)
|
||||
assert express(B.x, A) == A.x
|
||||
assert express(B.y, A) == (cos(q2)*A.y + sin(q2)*A.z)
|
||||
assert express(B.z, A) == (-sin(q2)*A.y + cos(q2)*A.z)
|
||||
assert express(B.x, B) == B.x
|
||||
assert express(B.y, B) == B.y
|
||||
assert express(B.z, B) == B.z
|
||||
assert express(B.x, C) == (cos(q3)*C.x + sin(q3)*C.z)
|
||||
assert express(B.y, C) == C.y
|
||||
assert express(B.z, C) == (-sin(q3)*C.x + cos(q3)*C.z)
|
||||
|
||||
assert express(C.x, N) == (
|
||||
(cos(q1)*cos(q3) - sin(q1)*sin(q2)*sin(q3))*N.x +
|
||||
(sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*N.y -
|
||||
sin(q3)*cos(q2)*N.z)
|
||||
assert express(C.y, N) == (
|
||||
-sin(q1)*cos(q2)*N.x + cos(q1)*cos(q2)*N.y + sin(q2)*N.z)
|
||||
assert express(C.z, N) == (
|
||||
(sin(q3)*cos(q1) + sin(q1)*sin(q2)*cos(q3))*N.x +
|
||||
(sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*N.y +
|
||||
cos(q2)*cos(q3)*N.z)
|
||||
assert express(C.x, A) == (cos(q3)*A.x + sin(q2)*sin(q3)*A.y -
|
||||
sin(q3)*cos(q2)*A.z)
|
||||
assert express(C.y, A) == (cos(q2)*A.y + sin(q2)*A.z)
|
||||
assert express(C.z, A) == (sin(q3)*A.x - sin(q2)*cos(q3)*A.y +
|
||||
cos(q2)*cos(q3)*A.z)
|
||||
assert express(C.x, B) == (cos(q3)*B.x - sin(q3)*B.z)
|
||||
assert express(C.y, B) == B.y
|
||||
assert express(C.z, B) == (sin(q3)*B.x + cos(q3)*B.z)
|
||||
assert express(C.x, C) == C.x
|
||||
assert express(C.y, C) == C.y
|
||||
assert express(C.z, C) == C.z == (C.z)
|
||||
|
||||
# Check to make sure Vectors get converted back to UnitVectors
|
||||
assert N.x == express((cos(q1)*A.x - sin(q1)*A.y), N).simplify()
|
||||
assert N.y == express((sin(q1)*A.x + cos(q1)*A.y), N).simplify()
|
||||
assert N.x == express((cos(q1)*B.x - sin(q1)*cos(q2)*B.y +
|
||||
sin(q1)*sin(q2)*B.z), N).simplify()
|
||||
assert N.y == express((sin(q1)*B.x + cos(q1)*cos(q2)*B.y -
|
||||
sin(q2)*cos(q1)*B.z), N).simplify()
|
||||
assert N.z == express((sin(q2)*B.y + cos(q2)*B.z), N).simplify()
|
||||
|
||||
"""
|
||||
These don't really test our code, they instead test the auto simplification
|
||||
(or lack thereof) of SymPy.
|
||||
assert N.x == express((
|
||||
(cos(q1)*cos(q3)-sin(q1)*sin(q2)*sin(q3))*C.x -
|
||||
sin(q1)*cos(q2)*C.y +
|
||||
(sin(q3)*cos(q1)+sin(q1)*sin(q2)*cos(q3))*C.z), N)
|
||||
assert N.y == express((
|
||||
(sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*C.x +
|
||||
cos(q1)*cos(q2)*C.y +
|
||||
(sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*C.z), N)
|
||||
assert N.z == express((-sin(q3)*cos(q2)*C.x + sin(q2)*C.y +
|
||||
cos(q2)*cos(q3)*C.z), N)
|
||||
"""
|
||||
|
||||
assert A.x == express((cos(q1)*N.x + sin(q1)*N.y), A).simplify()
|
||||
assert A.y == express((-sin(q1)*N.x + cos(q1)*N.y), A).simplify()
|
||||
|
||||
assert A.y == express((cos(q2)*B.y - sin(q2)*B.z), A).simplify()
|
||||
assert A.z == express((sin(q2)*B.y + cos(q2)*B.z), A).simplify()
|
||||
|
||||
assert A.x == express((cos(q3)*C.x + sin(q3)*C.z), A).simplify()
|
||||
|
||||
# Tripsimp messes up here too.
|
||||
#print express((sin(q2)*sin(q3)*C.x + cos(q2)*C.y -
|
||||
# sin(q2)*cos(q3)*C.z), A)
|
||||
assert A.y == express((sin(q2)*sin(q3)*C.x + cos(q2)*C.y -
|
||||
sin(q2)*cos(q3)*C.z), A).simplify()
|
||||
|
||||
assert A.z == express((-sin(q3)*cos(q2)*C.x + sin(q2)*C.y +
|
||||
cos(q2)*cos(q3)*C.z), A).simplify()
|
||||
assert B.x == express((cos(q1)*N.x + sin(q1)*N.y), B).simplify()
|
||||
assert B.y == express((-sin(q1)*cos(q2)*N.x +
|
||||
cos(q1)*cos(q2)*N.y + sin(q2)*N.z), B).simplify()
|
||||
|
||||
assert B.z == express((sin(q1)*sin(q2)*N.x -
|
||||
sin(q2)*cos(q1)*N.y + cos(q2)*N.z), B).simplify()
|
||||
|
||||
assert B.y == express((cos(q2)*A.y + sin(q2)*A.z), B).simplify()
|
||||
assert B.z == express((-sin(q2)*A.y + cos(q2)*A.z), B).simplify()
|
||||
assert B.x == express((cos(q3)*C.x + sin(q3)*C.z), B).simplify()
|
||||
assert B.z == express((-sin(q3)*C.x + cos(q3)*C.z), B).simplify()
|
||||
|
||||
"""
|
||||
assert C.x == express((
|
||||
(cos(q1)*cos(q3)-sin(q1)*sin(q2)*sin(q3))*N.x +
|
||||
(sin(q1)*cos(q3)+sin(q2)*sin(q3)*cos(q1))*N.y -
|
||||
sin(q3)*cos(q2)*N.z), C)
|
||||
assert C.y == express((
|
||||
-sin(q1)*cos(q2)*N.x + cos(q1)*cos(q2)*N.y + sin(q2)*N.z), C)
|
||||
assert C.z == express((
|
||||
(sin(q3)*cos(q1)+sin(q1)*sin(q2)*cos(q3))*N.x +
|
||||
(sin(q1)*sin(q3)-sin(q2)*cos(q1)*cos(q3))*N.y +
|
||||
cos(q2)*cos(q3)*N.z), C)
|
||||
"""
|
||||
assert C.x == express((cos(q3)*A.x + sin(q2)*sin(q3)*A.y -
|
||||
sin(q3)*cos(q2)*A.z), C).simplify()
|
||||
assert C.y == express((cos(q2)*A.y + sin(q2)*A.z), C).simplify()
|
||||
assert C.z == express((sin(q3)*A.x - sin(q2)*cos(q3)*A.y +
|
||||
cos(q2)*cos(q3)*A.z), C).simplify()
|
||||
assert C.x == express((cos(q3)*B.x - sin(q3)*B.z), C).simplify()
|
||||
assert C.z == express((sin(q3)*B.x + cos(q3)*B.z), C).simplify()
|
||||
|
||||
|
||||
def test_time_derivative():
|
||||
#The use of time_derivative for calculations pertaining to scalar
|
||||
#fields has been tested in test_coordinate_vars in test_essential.py
|
||||
A = ReferenceFrame('A')
|
||||
q = dynamicsymbols('q')
|
||||
qd = dynamicsymbols('q', 1)
|
||||
B = A.orientnew('B', 'Axis', [q, A.z])
|
||||
d = A.x | A.x
|
||||
assert time_derivative(d, B) == (-qd) * (A.y | A.x) + \
|
||||
(-qd) * (A.x | A.y)
|
||||
d1 = A.x | B.y
|
||||
assert time_derivative(d1, A) == - qd*(A.x|B.x)
|
||||
assert time_derivative(d1, B) == - qd*(A.y|B.y)
|
||||
d2 = A.x | B.x
|
||||
assert time_derivative(d2, A) == qd*(A.x|B.y)
|
||||
assert time_derivative(d2, B) == - qd*(A.y|B.x)
|
||||
d3 = A.x | B.z
|
||||
assert time_derivative(d3, A) == 0
|
||||
assert time_derivative(d3, B) == - qd*(A.y|B.z)
|
||||
q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4')
|
||||
q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1)
|
||||
q1dd, q2dd, q3dd, q4dd = dynamicsymbols('q1 q2 q3 q4', 2)
|
||||
C = B.orientnew('C', 'Axis', [q4, B.x])
|
||||
v1 = q1 * A.z
|
||||
v2 = q2*A.x + q3*B.y
|
||||
v3 = q1*A.x + q2*A.y + q3*A.z
|
||||
assert time_derivative(B.x, C) == 0
|
||||
assert time_derivative(B.y, C) == - q4d*B.z
|
||||
assert time_derivative(B.z, C) == q4d*B.y
|
||||
assert time_derivative(v1, B) == q1d*A.z
|
||||
assert time_derivative(v1, C) == - q1*sin(q)*q4d*A.x + \
|
||||
q1*cos(q)*q4d*A.y + q1d*A.z
|
||||
assert time_derivative(v2, A) == q2d*A.x - q3*qd*B.x + q3d*B.y
|
||||
assert time_derivative(v2, C) == q2d*A.x - q2*qd*A.y + \
|
||||
q2*sin(q)*q4d*A.z + q3d*B.y - q3*q4d*B.z
|
||||
assert time_derivative(v3, B) == (q2*qd + q1d)*A.x + \
|
||||
(-q1*qd + q2d)*A.y + q3d*A.z
|
||||
assert time_derivative(d, C) == - qd*(A.y|A.x) + \
|
||||
sin(q)*q4d*(A.z|A.x) - qd*(A.x|A.y) + sin(q)*q4d*(A.x|A.z)
|
||||
raises(ValueError, lambda: time_derivative(B.x, C, order=0.5))
|
||||
raises(ValueError, lambda: time_derivative(B.x, C, order=-1))
|
||||
|
||||
|
||||
def test_get_motion_methods():
|
||||
#Initialization
|
||||
t = dynamicsymbols._t
|
||||
s1, s2, s3 = symbols('s1 s2 s3')
|
||||
S1, S2, S3 = symbols('S1 S2 S3')
|
||||
S4, S5, S6 = symbols('S4 S5 S6')
|
||||
t1, t2 = symbols('t1 t2')
|
||||
a, b, c = dynamicsymbols('a b c')
|
||||
ad, bd, cd = dynamicsymbols('a b c', 1)
|
||||
a2d, b2d, c2d = dynamicsymbols('a b c', 2)
|
||||
v0 = S1*N.x + S2*N.y + S3*N.z
|
||||
v01 = S4*N.x + S5*N.y + S6*N.z
|
||||
v1 = s1*N.x + s2*N.y + s3*N.z
|
||||
v2 = a*N.x + b*N.y + c*N.z
|
||||
v2d = ad*N.x + bd*N.y + cd*N.z
|
||||
v2dd = a2d*N.x + b2d*N.y + c2d*N.z
|
||||
#Test position parameter
|
||||
assert get_motion_params(frame = N) == (0, 0, 0)
|
||||
assert get_motion_params(N, position=v1) == (0, 0, v1)
|
||||
assert get_motion_params(N, position=v2) == (v2dd, v2d, v2)
|
||||
#Test velocity parameter
|
||||
assert get_motion_params(N, velocity=v1) == (0, v1, v1 * t)
|
||||
assert get_motion_params(N, velocity=v1, position=v0, timevalue1=t1) == \
|
||||
(0, v1, v0 + v1*(t - t1))
|
||||
answer = get_motion_params(N, velocity=v1, position=v2, timevalue1=t1)
|
||||
answer_expected = (0, v1, v1*t - v1*t1 + v2.subs(t, t1))
|
||||
assert answer == answer_expected
|
||||
|
||||
answer = get_motion_params(N, velocity=v2, position=v0, timevalue1=t1)
|
||||
integral_vector = Integral(a, (t, t1, t))*N.x + Integral(b, (t, t1, t))*N.y \
|
||||
+ Integral(c, (t, t1, t))*N.z
|
||||
answer_expected = (v2d, v2, v0 + integral_vector)
|
||||
assert answer == answer_expected
|
||||
|
||||
#Test acceleration parameter
|
||||
assert get_motion_params(N, acceleration=v1) == \
|
||||
(v1, v1 * t, v1 * t**2/2)
|
||||
assert get_motion_params(N, acceleration=v1, velocity=v0,
|
||||
position=v2, timevalue1=t1, timevalue2=t2) == \
|
||||
(v1, (v0 + v1*t - v1*t2),
|
||||
-v0*t1 + v1*t**2/2 + v1*t2*t1 - \
|
||||
v1*t1**2/2 + t*(v0 - v1*t2) + \
|
||||
v2.subs(t, t1))
|
||||
assert get_motion_params(N, acceleration=v1, velocity=v0,
|
||||
position=v01, timevalue1=t1, timevalue2=t2) == \
|
||||
(v1, v0 + v1*t - v1*t2,
|
||||
-v0*t1 + v01 + v1*t**2/2 + \
|
||||
v1*t2*t1 - v1*t1**2/2 + \
|
||||
t*(v0 - v1*t2))
|
||||
answer = get_motion_params(N, acceleration=a*N.x, velocity=S1*N.x,
|
||||
position=S2*N.x, timevalue1=t1, timevalue2=t2)
|
||||
i1 = Integral(a, (t, t2, t))
|
||||
answer_expected = (a*N.x, (S1 + i1)*N.x, \
|
||||
(S2 + Integral(S1 + i1, (t, t1, t)))*N.x)
|
||||
assert answer == answer_expected
|
||||
|
||||
|
||||
def test_kin_eqs():
|
||||
q0, q1, q2, q3 = dynamicsymbols('q0 q1 q2 q3')
|
||||
q0d, q1d, q2d, q3d = dynamicsymbols('q0 q1 q2 q3', 1)
|
||||
u1, u2, u3 = dynamicsymbols('u1 u2 u3')
|
||||
ke = kinematic_equations([u1,u2,u3], [q1,q2,q3], 'body', 313)
|
||||
assert ke == kinematic_equations([u1,u2,u3], [q1,q2,q3], 'body', '313')
|
||||
kds = kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'quaternion')
|
||||
assert kds == [-0.5 * q0 * u1 - 0.5 * q2 * u3 + 0.5 * q3 * u2 + q1d,
|
||||
-0.5 * q0 * u2 + 0.5 * q1 * u3 - 0.5 * q3 * u1 + q2d,
|
||||
-0.5 * q0 * u3 - 0.5 * q1 * u2 + 0.5 * q2 * u1 + q3d,
|
||||
0.5 * q1 * u1 + 0.5 * q2 * u2 + 0.5 * q3 * u3 + q0d]
|
||||
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2], 'quaternion'))
|
||||
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'quaternion', '123'))
|
||||
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'foo'))
|
||||
raises(TypeError, lambda: kinematic_equations(u1, [q0, q1, q2, q3], 'quaternion'))
|
||||
raises(TypeError, lambda: kinematic_equations([u1], [q0, q1, q2, q3], 'quaternion'))
|
||||
raises(TypeError, lambda: kinematic_equations([u1, u2, u3], q0, 'quaternion'))
|
||||
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'body'))
|
||||
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2, q3], 'space'))
|
||||
raises(ValueError, lambda: kinematic_equations([u1, u2, u3], [q0, q1, q2], 'body', '222'))
|
||||
assert kinematic_equations([0, 0, 0], [q0, q1, q2], 'space') == [S.Zero, S.Zero, S.Zero]
|
||||
|
||||
|
||||
def test_partial_velocity():
|
||||
q1, q2, q3, u1, u2, u3 = dynamicsymbols('q1 q2 q3 u1 u2 u3')
|
||||
u4, u5 = dynamicsymbols('u4, u5')
|
||||
r = symbols('r')
|
||||
|
||||
N = ReferenceFrame('N')
|
||||
Y = N.orientnew('Y', 'Axis', [q1, N.z])
|
||||
L = Y.orientnew('L', 'Axis', [q2, Y.x])
|
||||
R = L.orientnew('R', 'Axis', [q3, L.y])
|
||||
R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z)
|
||||
|
||||
C = Point('C')
|
||||
C.set_vel(N, u4 * L.x + u5 * (Y.z ^ L.x))
|
||||
Dmc = C.locatenew('Dmc', r * L.z)
|
||||
Dmc.v2pt_theory(C, N, R)
|
||||
|
||||
vel_list = [Dmc.vel(N), C.vel(N), R.ang_vel_in(N)]
|
||||
u_list = [u1, u2, u3, u4, u5]
|
||||
assert (partial_velocity(vel_list, u_list, N) ==
|
||||
[[- r*L.y, r*L.x, 0, L.x, cos(q2)*L.y - sin(q2)*L.z],
|
||||
[0, 0, 0, L.x, cos(q2)*L.y - sin(q2)*L.z],
|
||||
[L.x, L.y, L.z, 0, 0]])
|
||||
|
||||
# Make sure that partial velocities can be computed regardless if the
|
||||
# orientation between frames is defined or not.
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
v = u4 * A.x + u5 * B.y
|
||||
assert partial_velocity((v, ), (u4, u5), A) == [[A.x, B.y]]
|
||||
|
||||
raises(TypeError, lambda: partial_velocity(Dmc.vel(N), u_list, N))
|
||||
raises(TypeError, lambda: partial_velocity(vel_list, u1, N))
|
||||
|
||||
def test_dynamicsymbols():
|
||||
#Tests to check the assumptions applied to dynamicsymbols
|
||||
f1 = dynamicsymbols('f1')
|
||||
f2 = dynamicsymbols('f2', real=True)
|
||||
f3 = dynamicsymbols('f3', positive=True)
|
||||
f4, f5 = dynamicsymbols('f4,f5', commutative=False)
|
||||
f6 = dynamicsymbols('f6', integer=True)
|
||||
assert f1.is_real is None
|
||||
assert f2.is_real
|
||||
assert f3.is_positive
|
||||
assert f4*f5 != f5*f4
|
||||
assert f6.is_integer
|
||||
@@ -0,0 +1,75 @@
|
||||
from sympy.core.singleton import S
|
||||
from sympy.physics.vector import Vector, ReferenceFrame, Dyadic
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
A = ReferenceFrame('A')
|
||||
|
||||
|
||||
def test_output_type():
|
||||
A = ReferenceFrame('A')
|
||||
v = A.x + A.y
|
||||
d = v | v
|
||||
zerov = Vector(0)
|
||||
zerod = Dyadic(0)
|
||||
|
||||
# dot products
|
||||
assert isinstance(d & d, Dyadic)
|
||||
assert isinstance(d & zerod, Dyadic)
|
||||
assert isinstance(zerod & d, Dyadic)
|
||||
assert isinstance(d & v, Vector)
|
||||
assert isinstance(v & d, Vector)
|
||||
assert isinstance(d & zerov, Vector)
|
||||
assert isinstance(zerov & d, Vector)
|
||||
raises(TypeError, lambda: d & S.Zero)
|
||||
raises(TypeError, lambda: S.Zero & d)
|
||||
raises(TypeError, lambda: d & 0)
|
||||
raises(TypeError, lambda: 0 & d)
|
||||
assert not isinstance(v & v, (Vector, Dyadic))
|
||||
assert not isinstance(v & zerov, (Vector, Dyadic))
|
||||
assert not isinstance(zerov & v, (Vector, Dyadic))
|
||||
raises(TypeError, lambda: v & S.Zero)
|
||||
raises(TypeError, lambda: S.Zero & v)
|
||||
raises(TypeError, lambda: v & 0)
|
||||
raises(TypeError, lambda: 0 & v)
|
||||
|
||||
# cross products
|
||||
raises(TypeError, lambda: d ^ d)
|
||||
raises(TypeError, lambda: d ^ zerod)
|
||||
raises(TypeError, lambda: zerod ^ d)
|
||||
assert isinstance(d ^ v, Dyadic)
|
||||
assert isinstance(v ^ d, Dyadic)
|
||||
assert isinstance(d ^ zerov, Dyadic)
|
||||
assert isinstance(zerov ^ d, Dyadic)
|
||||
assert isinstance(zerov ^ d, Dyadic)
|
||||
raises(TypeError, lambda: d ^ S.Zero)
|
||||
raises(TypeError, lambda: S.Zero ^ d)
|
||||
raises(TypeError, lambda: d ^ 0)
|
||||
raises(TypeError, lambda: 0 ^ d)
|
||||
assert isinstance(v ^ v, Vector)
|
||||
assert isinstance(v ^ zerov, Vector)
|
||||
assert isinstance(zerov ^ v, Vector)
|
||||
raises(TypeError, lambda: v ^ S.Zero)
|
||||
raises(TypeError, lambda: S.Zero ^ v)
|
||||
raises(TypeError, lambda: v ^ 0)
|
||||
raises(TypeError, lambda: 0 ^ v)
|
||||
|
||||
# outer products
|
||||
raises(TypeError, lambda: d | d)
|
||||
raises(TypeError, lambda: d | zerod)
|
||||
raises(TypeError, lambda: zerod | d)
|
||||
raises(TypeError, lambda: d | v)
|
||||
raises(TypeError, lambda: v | d)
|
||||
raises(TypeError, lambda: d | zerov)
|
||||
raises(TypeError, lambda: zerov | d)
|
||||
raises(TypeError, lambda: zerov | d)
|
||||
raises(TypeError, lambda: d | S.Zero)
|
||||
raises(TypeError, lambda: S.Zero | d)
|
||||
raises(TypeError, lambda: d | 0)
|
||||
raises(TypeError, lambda: 0 | d)
|
||||
assert isinstance(v | v, Dyadic)
|
||||
assert isinstance(v | zerov, Dyadic)
|
||||
assert isinstance(zerov | v, Dyadic)
|
||||
raises(TypeError, lambda: v | S.Zero)
|
||||
raises(TypeError, lambda: S.Zero | v)
|
||||
raises(TypeError, lambda: v | 0)
|
||||
raises(TypeError, lambda: 0 | v)
|
||||
@@ -0,0 +1,382 @@
|
||||
from sympy.physics.vector import dynamicsymbols, Point, ReferenceFrame
|
||||
from sympy.testing.pytest import raises, ignore_warnings
|
||||
import warnings
|
||||
|
||||
def test_point_v1pt_theorys():
|
||||
q, q2 = dynamicsymbols('q q2')
|
||||
qd, q2d = dynamicsymbols('q q2', 1)
|
||||
qdd, q2dd = dynamicsymbols('q q2', 2)
|
||||
N = ReferenceFrame('N')
|
||||
B = ReferenceFrame('B')
|
||||
B.set_ang_vel(N, qd * B.z)
|
||||
O = Point('O')
|
||||
P = O.locatenew('P', B.x)
|
||||
P.set_vel(B, 0)
|
||||
O.set_vel(N, 0)
|
||||
assert P.v1pt_theory(O, N, B) == qd * B.y
|
||||
O.set_vel(N, N.x)
|
||||
assert P.v1pt_theory(O, N, B) == N.x + qd * B.y
|
||||
P.set_vel(B, B.z)
|
||||
assert P.v1pt_theory(O, N, B) == B.z + N.x + qd * B.y
|
||||
|
||||
|
||||
def test_point_a1pt_theorys():
|
||||
q, q2 = dynamicsymbols('q q2')
|
||||
qd, q2d = dynamicsymbols('q q2', 1)
|
||||
qdd, q2dd = dynamicsymbols('q q2', 2)
|
||||
N = ReferenceFrame('N')
|
||||
B = ReferenceFrame('B')
|
||||
B.set_ang_vel(N, qd * B.z)
|
||||
O = Point('O')
|
||||
P = O.locatenew('P', B.x)
|
||||
P.set_vel(B, 0)
|
||||
O.set_vel(N, 0)
|
||||
assert P.a1pt_theory(O, N, B) == -(qd**2) * B.x + qdd * B.y
|
||||
P.set_vel(B, q2d * B.z)
|
||||
assert P.a1pt_theory(O, N, B) == -(qd**2) * B.x + qdd * B.y + q2dd * B.z
|
||||
O.set_vel(N, q2d * B.x)
|
||||
assert P.a1pt_theory(O, N, B) == ((q2dd - qd**2) * B.x + (q2d * qd + qdd) * B.y +
|
||||
q2dd * B.z)
|
||||
|
||||
|
||||
def test_point_v2pt_theorys():
|
||||
q = dynamicsymbols('q')
|
||||
qd = dynamicsymbols('q', 1)
|
||||
N = ReferenceFrame('N')
|
||||
B = N.orientnew('B', 'Axis', [q, N.z])
|
||||
O = Point('O')
|
||||
P = O.locatenew('P', 0)
|
||||
O.set_vel(N, 0)
|
||||
assert P.v2pt_theory(O, N, B) == 0
|
||||
P = O.locatenew('P', B.x)
|
||||
assert P.v2pt_theory(O, N, B) == (qd * B.z ^ B.x)
|
||||
O.set_vel(N, N.x)
|
||||
assert P.v2pt_theory(O, N, B) == N.x + qd * B.y
|
||||
|
||||
|
||||
def test_point_a2pt_theorys():
|
||||
q = dynamicsymbols('q')
|
||||
qd = dynamicsymbols('q', 1)
|
||||
qdd = dynamicsymbols('q', 2)
|
||||
N = ReferenceFrame('N')
|
||||
B = N.orientnew('B', 'Axis', [q, N.z])
|
||||
O = Point('O')
|
||||
P = O.locatenew('P', 0)
|
||||
O.set_vel(N, 0)
|
||||
assert P.a2pt_theory(O, N, B) == 0
|
||||
P.set_pos(O, B.x)
|
||||
assert P.a2pt_theory(O, N, B) == (-qd**2) * B.x + (qdd) * B.y
|
||||
|
||||
|
||||
def test_point_funcs():
|
||||
q, q2 = dynamicsymbols('q q2')
|
||||
qd, q2d = dynamicsymbols('q q2', 1)
|
||||
qdd, q2dd = dynamicsymbols('q q2', 2)
|
||||
N = ReferenceFrame('N')
|
||||
B = ReferenceFrame('B')
|
||||
B.set_ang_vel(N, 5 * B.y)
|
||||
O = Point('O')
|
||||
P = O.locatenew('P', q * B.x + q2 * B.y)
|
||||
assert P.pos_from(O) == q * B.x + q2 * B.y
|
||||
P.set_vel(B, qd * B.x + q2d * B.y)
|
||||
assert P.vel(B) == qd * B.x + q2d * B.y
|
||||
O.set_vel(N, 0)
|
||||
assert O.vel(N) == 0
|
||||
assert P.a1pt_theory(O, N, B) == ((-25 * q + qdd) * B.x + (q2dd) * B.y +
|
||||
(-10 * qd) * B.z)
|
||||
|
||||
B = N.orientnew('B', 'Axis', [q, N.z])
|
||||
O = Point('O')
|
||||
P = O.locatenew('P', 10 * B.x)
|
||||
O.set_vel(N, 5 * N.x)
|
||||
assert O.vel(N) == 5 * N.x
|
||||
assert P.a2pt_theory(O, N, B) == (-10 * qd**2) * B.x + (10 * qdd) * B.y
|
||||
|
||||
B.set_ang_vel(N, 5 * B.y)
|
||||
O = Point('O')
|
||||
P = O.locatenew('P', q * B.x + q2 * B.y)
|
||||
P.set_vel(B, qd * B.x + q2d * B.y)
|
||||
O.set_vel(N, 0)
|
||||
assert P.v1pt_theory(O, N, B) == qd * B.x + q2d * B.y - 5 * q * B.z
|
||||
|
||||
|
||||
def test_point_pos():
|
||||
q = dynamicsymbols('q')
|
||||
N = ReferenceFrame('N')
|
||||
B = N.orientnew('B', 'Axis', [q, N.z])
|
||||
O = Point('O')
|
||||
P = O.locatenew('P', 10 * N.x + 5 * B.x)
|
||||
assert P.pos_from(O) == 10 * N.x + 5 * B.x
|
||||
Q = P.locatenew('Q', 10 * N.y + 5 * B.y)
|
||||
assert Q.pos_from(P) == 10 * N.y + 5 * B.y
|
||||
assert Q.pos_from(O) == 10 * N.x + 10 * N.y + 5 * B.x + 5 * B.y
|
||||
assert O.pos_from(Q) == -10 * N.x - 10 * N.y - 5 * B.x - 5 * B.y
|
||||
|
||||
def test_point_partial_velocity():
|
||||
|
||||
N = ReferenceFrame('N')
|
||||
A = ReferenceFrame('A')
|
||||
|
||||
p = Point('p')
|
||||
|
||||
u1, u2 = dynamicsymbols('u1, u2')
|
||||
|
||||
p.set_vel(N, u1 * A.x + u2 * N.y)
|
||||
|
||||
assert p.partial_velocity(N, u1) == A.x
|
||||
assert p.partial_velocity(N, u1, u2) == (A.x, N.y)
|
||||
raises(ValueError, lambda: p.partial_velocity(A, u1))
|
||||
|
||||
def test_point_vel(): #Basic functionality
|
||||
q1, q2 = dynamicsymbols('q1 q2')
|
||||
N = ReferenceFrame('N')
|
||||
B = ReferenceFrame('B')
|
||||
Q = Point('Q')
|
||||
O = Point('O')
|
||||
Q.set_pos(O, q1 * N.x)
|
||||
raises(ValueError , lambda: Q.vel(N)) # Velocity of O in N is not defined
|
||||
O.set_vel(N, q2 * N.y)
|
||||
assert O.vel(N) == q2 * N.y
|
||||
raises(ValueError , lambda : O.vel(B)) #Velocity of O is not defined in B
|
||||
|
||||
def test_auto_point_vel():
|
||||
t = dynamicsymbols._t
|
||||
q1, q2 = dynamicsymbols('q1 q2')
|
||||
N = ReferenceFrame('N')
|
||||
B = ReferenceFrame('B')
|
||||
O = Point('O')
|
||||
Q = Point('Q')
|
||||
Q.set_pos(O, q1 * N.x)
|
||||
O.set_vel(N, q2 * N.y)
|
||||
assert Q.vel(N) == q1.diff(t) * N.x + q2 * N.y # Velocity of Q using O
|
||||
P1 = Point('P1')
|
||||
P1.set_pos(O, q1 * B.x)
|
||||
P2 = Point('P2')
|
||||
P2.set_pos(P1, q2 * B.z)
|
||||
raises(ValueError, lambda : P2.vel(B)) # O's velocity is defined in different frame, and no
|
||||
#point in between has its velocity defined
|
||||
raises(ValueError, lambda: P2.vel(N)) # Velocity of O not defined in N
|
||||
|
||||
def test_auto_point_vel_multiple_point_path():
|
||||
t = dynamicsymbols._t
|
||||
q1, q2 = dynamicsymbols('q1 q2')
|
||||
B = ReferenceFrame('B')
|
||||
P = Point('P')
|
||||
P.set_vel(B, q1 * B.x)
|
||||
P1 = Point('P1')
|
||||
P1.set_pos(P, q2 * B.y)
|
||||
P1.set_vel(B, q1 * B.z)
|
||||
P2 = Point('P2')
|
||||
P2.set_pos(P1, q1 * B.z)
|
||||
P3 = Point('P3')
|
||||
P3.set_pos(P2, 10 * q1 * B.y)
|
||||
assert P3.vel(B) == 10 * q1.diff(t) * B.y + (q1 + q1.diff(t)) * B.z
|
||||
|
||||
def test_auto_vel_dont_overwrite():
|
||||
t = dynamicsymbols._t
|
||||
q1, q2, u1 = dynamicsymbols('q1, q2, u1')
|
||||
N = ReferenceFrame('N')
|
||||
P = Point('P1')
|
||||
P.set_vel(N, u1 * N.x)
|
||||
P1 = Point('P1')
|
||||
P1.set_pos(P, q2 * N.y)
|
||||
assert P1.vel(N) == q2.diff(t) * N.y + u1 * N.x
|
||||
assert P.vel(N) == u1 * N.x
|
||||
P1.set_vel(N, u1 * N.z)
|
||||
assert P1.vel(N) == u1 * N.z
|
||||
|
||||
def test_auto_point_vel_if_tree_has_vel_but_inappropriate_pos_vector():
|
||||
q1, q2 = dynamicsymbols('q1 q2')
|
||||
B = ReferenceFrame('B')
|
||||
S = ReferenceFrame('S')
|
||||
P = Point('P')
|
||||
P.set_vel(B, q1 * B.x)
|
||||
P1 = Point('P1')
|
||||
P1.set_pos(P, S.y)
|
||||
raises(ValueError, lambda : P1.vel(B)) # P1.pos_from(P) can't be expressed in B
|
||||
raises(ValueError, lambda : P1.vel(S)) # P.vel(S) not defined
|
||||
|
||||
def test_auto_point_vel_shortest_path():
|
||||
t = dynamicsymbols._t
|
||||
q1, q2, u1, u2 = dynamicsymbols('q1 q2 u1 u2')
|
||||
B = ReferenceFrame('B')
|
||||
P = Point('P')
|
||||
P.set_vel(B, u1 * B.x)
|
||||
P1 = Point('P1')
|
||||
P1.set_pos(P, q2 * B.y)
|
||||
P1.set_vel(B, q1 * B.z)
|
||||
P2 = Point('P2')
|
||||
P2.set_pos(P1, q1 * B.z)
|
||||
P3 = Point('P3')
|
||||
P3.set_pos(P2, 10 * q1 * B.y)
|
||||
P4 = Point('P4')
|
||||
P4.set_pos(P3, q1 * B.x)
|
||||
O = Point('O')
|
||||
O.set_vel(B, u2 * B.y)
|
||||
O1 = Point('O1')
|
||||
O1.set_pos(O, q2 * B.z)
|
||||
P4.set_pos(O1, q1 * B.x + q2 * B.z)
|
||||
with warnings.catch_warnings(): #There are two possible paths in this point tree, thus a warning is raised
|
||||
warnings.simplefilter('error')
|
||||
with ignore_warnings(UserWarning):
|
||||
assert P4.vel(B) == q1.diff(t) * B.x + u2 * B.y + 2 * q2.diff(t) * B.z
|
||||
|
||||
def test_auto_point_vel_connected_frames():
|
||||
t = dynamicsymbols._t
|
||||
q, q1, q2, u = dynamicsymbols('q q1 q2 u')
|
||||
N = ReferenceFrame('N')
|
||||
B = ReferenceFrame('B')
|
||||
O = Point('O')
|
||||
O.set_vel(N, u * N.x)
|
||||
P = Point('P')
|
||||
P.set_pos(O, q1 * N.x + q2 * B.y)
|
||||
raises(ValueError, lambda: P.vel(N))
|
||||
N.orient(B, 'Axis', (q, B.x))
|
||||
assert P.vel(N) == (u + q1.diff(t)) * N.x + q2.diff(t) * B.y - q2 * q.diff(t) * B.z
|
||||
|
||||
def test_auto_point_vel_multiple_paths_warning_arises():
|
||||
q, u = dynamicsymbols('q u')
|
||||
N = ReferenceFrame('N')
|
||||
O = Point('O')
|
||||
P = Point('P')
|
||||
Q = Point('Q')
|
||||
R = Point('R')
|
||||
P.set_vel(N, u * N.x)
|
||||
Q.set_vel(N, u *N.y)
|
||||
R.set_vel(N, u * N.z)
|
||||
O.set_pos(P, q * N.z)
|
||||
O.set_pos(Q, q * N.y)
|
||||
O.set_pos(R, q * N.x)
|
||||
with warnings.catch_warnings(): #There are two possible paths in this point tree, thus a warning is raised
|
||||
warnings.simplefilter("error")
|
||||
raises(UserWarning ,lambda: O.vel(N))
|
||||
|
||||
def test_auto_vel_cyclic_warning_arises():
|
||||
P = Point('P')
|
||||
P1 = Point('P1')
|
||||
P2 = Point('P2')
|
||||
P3 = Point('P3')
|
||||
N = ReferenceFrame('N')
|
||||
P.set_vel(N, N.x)
|
||||
P1.set_pos(P, N.x)
|
||||
P2.set_pos(P1, N.y)
|
||||
P3.set_pos(P2, N.z)
|
||||
P1.set_pos(P3, N.x + N.y)
|
||||
with warnings.catch_warnings(): #The path is cyclic at P1, thus a warning is raised
|
||||
warnings.simplefilter("error")
|
||||
raises(UserWarning ,lambda: P2.vel(N))
|
||||
|
||||
def test_auto_vel_cyclic_warning_msg():
|
||||
P = Point('P')
|
||||
P1 = Point('P1')
|
||||
P2 = Point('P2')
|
||||
P3 = Point('P3')
|
||||
N = ReferenceFrame('N')
|
||||
P.set_vel(N, N.x)
|
||||
P1.set_pos(P, N.x)
|
||||
P2.set_pos(P1, N.y)
|
||||
P3.set_pos(P2, N.z)
|
||||
P1.set_pos(P3, N.x + N.y)
|
||||
with warnings.catch_warnings(record = True) as w: #The path is cyclic at P1, thus a warning is raised
|
||||
warnings.simplefilter("always")
|
||||
P2.vel(N)
|
||||
msg = str(w[-1].message).replace("\n", " ")
|
||||
assert issubclass(w[-1].category, UserWarning)
|
||||
assert 'Kinematic loops are defined among the positions of points. This is likely not desired and may cause errors in your calculations.' in msg
|
||||
|
||||
def test_auto_vel_multiple_path_warning_msg():
|
||||
N = ReferenceFrame('N')
|
||||
O = Point('O')
|
||||
P = Point('P')
|
||||
Q = Point('Q')
|
||||
P.set_vel(N, N.x)
|
||||
Q.set_vel(N, N.y)
|
||||
O.set_pos(P, N.z)
|
||||
O.set_pos(Q, N.y)
|
||||
with warnings.catch_warnings(record = True) as w: #There are two possible paths in this point tree, thus a warning is raised
|
||||
warnings.simplefilter("always")
|
||||
O.vel(N)
|
||||
msg = str(w[-1].message).replace("\n", " ")
|
||||
assert issubclass(w[-1].category, UserWarning)
|
||||
assert 'Velocity' in msg
|
||||
assert 'automatically calculated based on point' in msg
|
||||
assert 'Velocities from these points are not necessarily the same. This may cause errors in your calculations.' in msg
|
||||
|
||||
def test_auto_vel_derivative():
|
||||
q1, q2 = dynamicsymbols('q1:3')
|
||||
u1, u2 = dynamicsymbols('u1:3', 1)
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
C = ReferenceFrame('C')
|
||||
B.orient_axis(A, A.z, q1)
|
||||
B.set_ang_vel(A, u1 * A.z)
|
||||
C.orient_axis(B, B.z, q2)
|
||||
C.set_ang_vel(B, u2 * B.z)
|
||||
|
||||
Am = Point('Am')
|
||||
Am.set_vel(A, 0)
|
||||
Bm = Point('Bm')
|
||||
Bm.set_pos(Am, B.x)
|
||||
Bm.set_vel(B, 0)
|
||||
Bm.set_vel(C, 0)
|
||||
Cm = Point('Cm')
|
||||
Cm.set_pos(Bm, C.x)
|
||||
Cm.set_vel(C, 0)
|
||||
temp = Cm._vel_dict.copy()
|
||||
assert Cm.vel(A) == (u1 * B.y + (u1 + u2) * C.y)
|
||||
Cm._vel_dict = temp
|
||||
Cm.v2pt_theory(Bm, B, C)
|
||||
assert Cm.vel(A) == (u1 * B.y + (u1 + u2) * C.y)
|
||||
|
||||
def test_auto_point_acc_zero_vel():
|
||||
N = ReferenceFrame('N')
|
||||
O = Point('O')
|
||||
O.set_vel(N, 0)
|
||||
assert O.acc(N) == 0 * N.x
|
||||
|
||||
def test_auto_point_acc_compute_vel():
|
||||
t = dynamicsymbols._t
|
||||
q1 = dynamicsymbols('q1')
|
||||
N = ReferenceFrame('N')
|
||||
A = ReferenceFrame('A')
|
||||
A.orient_axis(N, N.z, q1)
|
||||
|
||||
O = Point('O')
|
||||
O.set_vel(N, 0)
|
||||
P = Point('P')
|
||||
P.set_pos(O, A.x)
|
||||
assert P.acc(N) == -q1.diff(t) ** 2 * A.x + q1.diff(t, 2) * A.y
|
||||
|
||||
def test_auto_acc_derivative():
|
||||
# Tests whether the Point.acc method gives the correct acceleration of the
|
||||
# end point of two linkages in series, while getting minimal information.
|
||||
q1, q2 = dynamicsymbols('q1:3')
|
||||
u1, u2 = dynamicsymbols('q1:3', 1)
|
||||
v1, v2 = dynamicsymbols('q1:3', 2)
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
C = ReferenceFrame('C')
|
||||
B.orient_axis(A, A.z, q1)
|
||||
C.orient_axis(B, B.z, q2)
|
||||
|
||||
Am = Point('Am')
|
||||
Am.set_vel(A, 0)
|
||||
Bm = Point('Bm')
|
||||
Bm.set_pos(Am, B.x)
|
||||
Bm.set_vel(B, 0)
|
||||
Bm.set_vel(C, 0)
|
||||
Cm = Point('Cm')
|
||||
Cm.set_pos(Bm, C.x)
|
||||
Cm.set_vel(C, 0)
|
||||
|
||||
# Copy dictionaries to later check the calculation using the 2pt_theories
|
||||
Bm_vel_dict, Cm_vel_dict = Bm._vel_dict.copy(), Cm._vel_dict.copy()
|
||||
Bm_acc_dict, Cm_acc_dict = Bm._acc_dict.copy(), Cm._acc_dict.copy()
|
||||
check = -u1 ** 2 * B.x + v1 * B.y - (u1 + u2) ** 2 * C.x + (v1 + v2) * C.y
|
||||
assert Cm.acc(A) == check
|
||||
Bm._vel_dict, Cm._vel_dict = Bm_vel_dict, Cm_vel_dict
|
||||
Bm._acc_dict, Cm._acc_dict = Bm_acc_dict, Cm_acc_dict
|
||||
Bm.v2pt_theory(Am, A, B)
|
||||
Cm.v2pt_theory(Bm, A, C)
|
||||
Bm.a2pt_theory(Am, A, B)
|
||||
assert Cm.a2pt_theory(Bm, A, C) == check
|
||||
@@ -0,0 +1,353 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from sympy.core.function import Function
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (asin, cos, sin)
|
||||
from sympy.physics.vector import ReferenceFrame, dynamicsymbols, Dyadic
|
||||
from sympy.physics.vector.printing import (VectorLatexPrinter, vpprint,
|
||||
vsprint, vsstrrepr, vlatex)
|
||||
|
||||
|
||||
a, b, c = symbols('a, b, c')
|
||||
alpha, omega, beta = dynamicsymbols('alpha, omega, beta')
|
||||
|
||||
A = ReferenceFrame('A')
|
||||
N = ReferenceFrame('N')
|
||||
|
||||
v = a ** 2 * N.x + b * N.y + c * sin(alpha) * N.z
|
||||
w = alpha * N.x + sin(omega) * N.y + alpha * beta * N.z
|
||||
ww = alpha * N.x + asin(omega) * N.y - alpha.diff() * beta * N.z
|
||||
o = a/b * N.x + (c+b)/a * N.y + c**2/b * N.z
|
||||
|
||||
y = a ** 2 * (N.x | N.y) + b * (N.y | N.y) + c * sin(alpha) * (N.z | N.y)
|
||||
x = alpha * (N.x | N.x) + sin(omega) * (N.y | N.z) + alpha * beta * (N.z | N.x)
|
||||
xx = N.x | (-N.y - N.z)
|
||||
xx2 = N.x | (N.y + N.z)
|
||||
|
||||
def ascii_vpretty(expr):
|
||||
return vpprint(expr, use_unicode=False, wrap_line=False)
|
||||
|
||||
|
||||
def unicode_vpretty(expr):
|
||||
return vpprint(expr, use_unicode=True, wrap_line=False)
|
||||
|
||||
|
||||
def test_latex_printer():
|
||||
r = Function('r')('t')
|
||||
assert VectorLatexPrinter().doprint(r ** 2) == "r^{2}"
|
||||
r2 = Function('r^2')('t')
|
||||
assert VectorLatexPrinter().doprint(r2.diff()) == r'\dot{r^{2}}'
|
||||
ra = Function('r__a')('t')
|
||||
assert VectorLatexPrinter().doprint(ra.diff().diff()) == r'\ddot{r^{a}}'
|
||||
|
||||
|
||||
def test_vector_pretty_print():
|
||||
|
||||
# TODO : The unit vectors should print with subscripts but they just
|
||||
# print as `n_x` instead of making `x` a subscript with unicode.
|
||||
|
||||
# TODO : The pretty print division does not print correctly here:
|
||||
# w = alpha * N.x + sin(omega) * N.y + alpha / beta * N.z
|
||||
|
||||
expected = """\
|
||||
2 \n\
|
||||
a n_x + b n_y + c*sin(alpha) n_z\
|
||||
"""
|
||||
uexpected = """\
|
||||
2 \n\
|
||||
a n_x + b n_y + c⋅sin(α) n_z\
|
||||
"""
|
||||
|
||||
assert ascii_vpretty(v) == expected
|
||||
assert unicode_vpretty(v) == uexpected
|
||||
|
||||
expected = 'alpha n_x + sin(omega) n_y + alpha*beta n_z'
|
||||
uexpected = 'α n_x + sin(ω) n_y + α⋅β n_z'
|
||||
|
||||
assert ascii_vpretty(w) == expected
|
||||
assert unicode_vpretty(w) == uexpected
|
||||
|
||||
expected = """\
|
||||
2 \n\
|
||||
a b + c c \n\
|
||||
- n_x + ----- n_y + -- n_z\n\
|
||||
b a b \
|
||||
"""
|
||||
uexpected = """\
|
||||
2 \n\
|
||||
a b + c c \n\
|
||||
─ n_x + ───── n_y + ── n_z\n\
|
||||
b a b \
|
||||
"""
|
||||
|
||||
assert ascii_vpretty(o) == expected
|
||||
assert unicode_vpretty(o) == uexpected
|
||||
|
||||
# https://github.com/sympy/sympy/issues/26731
|
||||
assert ascii_vpretty(-A.x) == '-a_x'
|
||||
assert unicode_vpretty(-A.x) == '-a_x'
|
||||
|
||||
# https://github.com/sympy/sympy/issues/26799
|
||||
assert ascii_vpretty(0*A.x) == '0'
|
||||
assert unicode_vpretty(0*A.x) == '0'
|
||||
|
||||
|
||||
def test_vector_latex():
|
||||
|
||||
a, b, c, d, omega = symbols('a, b, c, d, omega')
|
||||
|
||||
v = (a ** 2 + b / c) * A.x + sqrt(d) * A.y + cos(omega) * A.z
|
||||
|
||||
assert vlatex(v) == (r'(a^{2} + \frac{b}{c})\mathbf{\hat{a}_x} + '
|
||||
r'\sqrt{d}\mathbf{\hat{a}_y} + '
|
||||
r'\cos{\left(\omega \right)}'
|
||||
r'\mathbf{\hat{a}_z}')
|
||||
|
||||
theta, omega, alpha, q = dynamicsymbols('theta, omega, alpha, q')
|
||||
|
||||
v = theta * A.x + omega * omega * A.y + (q * alpha) * A.z
|
||||
|
||||
assert vlatex(v) == (r'\theta\mathbf{\hat{a}_x} + '
|
||||
r'\omega^{2}\mathbf{\hat{a}_y} + '
|
||||
r'\alpha q\mathbf{\hat{a}_z}')
|
||||
|
||||
phi1, phi2, phi3 = dynamicsymbols('phi1, phi2, phi3')
|
||||
theta1, theta2, theta3 = symbols('theta1, theta2, theta3')
|
||||
|
||||
v = (sin(theta1) * A.x +
|
||||
cos(phi1) * cos(phi2) * A.y +
|
||||
cos(theta1 + phi3) * A.z)
|
||||
|
||||
assert vlatex(v) == (r'\sin{\left(\theta_{1} \right)}'
|
||||
r'\mathbf{\hat{a}_x} + \cos{'
|
||||
r'\left(\phi_{1} \right)} \cos{'
|
||||
r'\left(\phi_{2} \right)}\mathbf{\hat{a}_y} + '
|
||||
r'\cos{\left(\theta_{1} + '
|
||||
r'\phi_{3} \right)}\mathbf{\hat{a}_z}')
|
||||
|
||||
N = ReferenceFrame('N')
|
||||
|
||||
a, b, c, d, omega = symbols('a, b, c, d, omega')
|
||||
|
||||
v = (a ** 2 + b / c) * N.x + sqrt(d) * N.y + cos(omega) * N.z
|
||||
|
||||
expected = (r'(a^{2} + \frac{b}{c})\mathbf{\hat{n}_x} + '
|
||||
r'\sqrt{d}\mathbf{\hat{n}_y} + '
|
||||
r'\cos{\left(\omega \right)}'
|
||||
r'\mathbf{\hat{n}_z}')
|
||||
|
||||
assert vlatex(v) == expected
|
||||
|
||||
# Try custom unit vectors.
|
||||
|
||||
N = ReferenceFrame('N', latexs=(r'\hat{i}', r'\hat{j}', r'\hat{k}'))
|
||||
|
||||
v = (a ** 2 + b / c) * N.x + sqrt(d) * N.y + cos(omega) * N.z
|
||||
|
||||
expected = (r'(a^{2} + \frac{b}{c})\hat{i} + '
|
||||
r'\sqrt{d}\hat{j} + '
|
||||
r'\cos{\left(\omega \right)}\hat{k}')
|
||||
assert vlatex(v) == expected
|
||||
|
||||
expected = r'\alpha\mathbf{\hat{n}_x} + \operatorname{asin}{\left(\omega ' \
|
||||
r'\right)}\mathbf{\hat{n}_y} - \beta \dot{\alpha}\mathbf{\hat{n}_z}'
|
||||
assert vlatex(ww) == expected
|
||||
|
||||
expected = r'- \mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_y} - ' \
|
||||
r'\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_z}'
|
||||
assert vlatex(xx) == expected
|
||||
|
||||
expected = r'\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_y} + ' \
|
||||
r'\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_z}'
|
||||
assert vlatex(xx2) == expected
|
||||
|
||||
|
||||
def test_vector_latex_arguments():
|
||||
assert vlatex(N.x * 3.0, full_prec=False) == r'3.0\mathbf{\hat{n}_x}'
|
||||
assert vlatex(N.x * 3.0, full_prec=True) == r'3.00000000000000\mathbf{\hat{n}_x}'
|
||||
|
||||
|
||||
def test_vector_latex_with_functions():
|
||||
|
||||
N = ReferenceFrame('N')
|
||||
|
||||
omega, alpha = dynamicsymbols('omega, alpha')
|
||||
|
||||
v = omega.diff() * N.x
|
||||
|
||||
assert vlatex(v) == r'\dot{\omega}\mathbf{\hat{n}_x}'
|
||||
|
||||
v = omega.diff() ** alpha * N.x
|
||||
|
||||
assert vlatex(v) == (r'\dot{\omega}^{\alpha}'
|
||||
r'\mathbf{\hat{n}_x}')
|
||||
|
||||
|
||||
def test_dyadic_pretty_print():
|
||||
|
||||
expected = """\
|
||||
2
|
||||
a n_x|n_y + b n_y|n_y + c*sin(alpha) n_z|n_y\
|
||||
"""
|
||||
|
||||
uexpected = """\
|
||||
2
|
||||
a n_x⊗n_y + b n_y⊗n_y + c⋅sin(α) n_z⊗n_y\
|
||||
"""
|
||||
assert ascii_vpretty(y) == expected
|
||||
assert unicode_vpretty(y) == uexpected
|
||||
|
||||
expected = 'alpha n_x|n_x + sin(omega) n_y|n_z + alpha*beta n_z|n_x'
|
||||
uexpected = 'α n_x⊗n_x + sin(ω) n_y⊗n_z + α⋅β n_z⊗n_x'
|
||||
assert ascii_vpretty(x) == expected
|
||||
assert unicode_vpretty(x) == uexpected
|
||||
|
||||
assert ascii_vpretty(Dyadic([])) == '0'
|
||||
assert unicode_vpretty(Dyadic([])) == '0'
|
||||
|
||||
assert ascii_vpretty(xx) == '- n_x|n_y - n_x|n_z'
|
||||
assert unicode_vpretty(xx) == '- n_x⊗n_y - n_x⊗n_z'
|
||||
|
||||
assert ascii_vpretty(xx2) == 'n_x|n_y + n_x|n_z'
|
||||
assert unicode_vpretty(xx2) == 'n_x⊗n_y + n_x⊗n_z'
|
||||
|
||||
|
||||
def test_dyadic_latex():
|
||||
|
||||
expected = (r'a^{2}\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_y} + '
|
||||
r'b\mathbf{\hat{n}_y}\otimes \mathbf{\hat{n}_y} + '
|
||||
r'c \sin{\left(\alpha \right)}'
|
||||
r'\mathbf{\hat{n}_z}\otimes \mathbf{\hat{n}_y}')
|
||||
|
||||
assert vlatex(y) == expected
|
||||
|
||||
expected = (r'\alpha\mathbf{\hat{n}_x}\otimes \mathbf{\hat{n}_x} + '
|
||||
r'\sin{\left(\omega \right)}\mathbf{\hat{n}_y}'
|
||||
r'\otimes \mathbf{\hat{n}_z} + '
|
||||
r'\alpha \beta\mathbf{\hat{n}_z}\otimes \mathbf{\hat{n}_x}')
|
||||
|
||||
assert vlatex(x) == expected
|
||||
|
||||
assert vlatex(Dyadic([])) == '0'
|
||||
|
||||
|
||||
def test_dyadic_str():
|
||||
assert vsprint(Dyadic([])) == '0'
|
||||
assert vsprint(y) == 'a**2*(N.x|N.y) + b*(N.y|N.y) + c*sin(alpha)*(N.z|N.y)'
|
||||
assert vsprint(x) == 'alpha*(N.x|N.x) + sin(omega)*(N.y|N.z) + alpha*beta*(N.z|N.x)'
|
||||
assert vsprint(ww) == "alpha*N.x + asin(omega)*N.y - beta*alpha'*N.z"
|
||||
assert vsprint(xx) == '- (N.x|N.y) - (N.x|N.z)'
|
||||
assert vsprint(xx2) == '(N.x|N.y) + (N.x|N.z)'
|
||||
|
||||
|
||||
def test_vlatex(): # vlatex is broken #12078
|
||||
from sympy.physics.vector import vlatex
|
||||
|
||||
x = symbols('x')
|
||||
J = symbols('J')
|
||||
|
||||
f = Function('f')
|
||||
g = Function('g')
|
||||
h = Function('h')
|
||||
|
||||
expected = r'J \left(\frac{d}{d x} g{\left(x \right)} - \frac{d}{d x} h{\left(x \right)}\right)'
|
||||
|
||||
expr = J*f(x).diff(x).subs(f(x), g(x)-h(x))
|
||||
|
||||
assert vlatex(expr) == expected
|
||||
|
||||
|
||||
def test_issue_13354():
|
||||
"""
|
||||
Test for proper pretty printing of physics vectors with ADD
|
||||
instances in arguments.
|
||||
|
||||
Test is exactly the one suggested in the original bug report by
|
||||
@moorepants.
|
||||
"""
|
||||
|
||||
a, b, c = symbols('a, b, c')
|
||||
A = ReferenceFrame('A')
|
||||
v = a * A.x + b * A.y + c * A.z
|
||||
w = b * A.x + c * A.y + a * A.z
|
||||
z = w + v
|
||||
|
||||
expected = """(a + b) a_x + (b + c) a_y + (a + c) a_z"""
|
||||
|
||||
assert ascii_vpretty(z) == expected
|
||||
|
||||
|
||||
def test_vector_derivative_printing():
|
||||
# First order
|
||||
v = omega.diff() * N.x
|
||||
assert unicode_vpretty(v) == 'ω̇ n_x'
|
||||
assert ascii_vpretty(v) == "omega'(t) n_x"
|
||||
|
||||
# Second order
|
||||
v = omega.diff().diff() * N.x
|
||||
|
||||
assert vlatex(v) == r'\ddot{\omega}\mathbf{\hat{n}_x}'
|
||||
assert unicode_vpretty(v) == 'ω̈ n_x'
|
||||
assert ascii_vpretty(v) == "omega''(t) n_x"
|
||||
|
||||
# Third order
|
||||
v = omega.diff().diff().diff() * N.x
|
||||
|
||||
assert vlatex(v) == r'\dddot{\omega}\mathbf{\hat{n}_x}'
|
||||
assert unicode_vpretty(v) == 'ω⃛ n_x'
|
||||
assert ascii_vpretty(v) == "omega'''(t) n_x"
|
||||
|
||||
# Fourth order
|
||||
v = omega.diff().diff().diff().diff() * N.x
|
||||
|
||||
assert vlatex(v) == r'\ddddot{\omega}\mathbf{\hat{n}_x}'
|
||||
assert unicode_vpretty(v) == 'ω⃜ n_x'
|
||||
assert ascii_vpretty(v) == "omega''''(t) n_x"
|
||||
|
||||
# Fifth order
|
||||
v = omega.diff().diff().diff().diff().diff() * N.x
|
||||
|
||||
assert vlatex(v) == r'\frac{d^{5}}{d t^{5}} \omega\mathbf{\hat{n}_x}'
|
||||
expected = '''\
|
||||
5 \n\
|
||||
d \n\
|
||||
---(omega) n_x\n\
|
||||
5 \n\
|
||||
dt \
|
||||
'''
|
||||
uexpected = '''\
|
||||
5 \n\
|
||||
d \n\
|
||||
───(ω) n_x\n\
|
||||
5 \n\
|
||||
dt \
|
||||
'''
|
||||
assert unicode_vpretty(v) == uexpected
|
||||
assert ascii_vpretty(v) == expected
|
||||
|
||||
|
||||
def test_vector_str_printing():
|
||||
assert vsprint(w) == 'alpha*N.x + sin(omega)*N.y + alpha*beta*N.z'
|
||||
assert vsprint(omega.diff() * N.x) == "omega'*N.x"
|
||||
assert vsstrrepr(w) == 'alpha*N.x + sin(omega)*N.y + alpha*beta*N.z'
|
||||
|
||||
|
||||
def test_vector_str_arguments():
|
||||
assert vsprint(N.x * 3.0, full_prec=False) == '3.0*N.x'
|
||||
assert vsprint(N.x * 3.0, full_prec=True) == '3.00000000000000*N.x'
|
||||
|
||||
|
||||
def test_issue_14041():
|
||||
import sympy.physics.mechanics as me
|
||||
|
||||
A_frame = me.ReferenceFrame('A')
|
||||
thetad, phid = me.dynamicsymbols('theta, phi', 1)
|
||||
L = symbols('L')
|
||||
|
||||
assert vlatex(L*(phid + thetad)**2*A_frame.x) == \
|
||||
r"L \left(\dot{\phi} + \dot{\theta}\right)^{2}\mathbf{\hat{a}_x}"
|
||||
assert vlatex((phid + thetad)**2*A_frame.x) == \
|
||||
r"\left(\dot{\phi} + \dot{\theta}\right)^{2}\mathbf{\hat{a}_x}"
|
||||
assert vlatex((phid*thetad)**a*A_frame.x) == \
|
||||
r"\left(\dot{\phi} \dot{\theta}\right)^{a}\mathbf{\hat{a}_x}"
|
||||
@@ -0,0 +1,274 @@
|
||||
from sympy.core.numbers import (Float, pi)
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.core.sorting import ordered
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
||||
from sympy.physics.vector import ReferenceFrame, Vector, dynamicsymbols, dot
|
||||
from sympy.physics.vector.vector import VectorTypeError
|
||||
from sympy.abc import x, y, z
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
A = ReferenceFrame('A')
|
||||
|
||||
|
||||
def test_free_dynamicsymbols():
|
||||
A, B, C, D = symbols('A, B, C, D', cls=ReferenceFrame)
|
||||
a, b, c, d, e, f = dynamicsymbols('a, b, c, d, e, f')
|
||||
B.orient_axis(A, a, A.x)
|
||||
C.orient_axis(B, b, B.y)
|
||||
D.orient_axis(C, c, C.x)
|
||||
|
||||
v = d*D.x + e*D.y + f*D.z
|
||||
|
||||
assert set(ordered(v.free_dynamicsymbols(A))) == {a, b, c, d, e, f}
|
||||
assert set(ordered(v.free_dynamicsymbols(B))) == {b, c, d, e, f}
|
||||
assert set(ordered(v.free_dynamicsymbols(C))) == {c, d, e, f}
|
||||
assert set(ordered(v.free_dynamicsymbols(D))) == {d, e, f}
|
||||
|
||||
|
||||
def test_Vector():
|
||||
assert A.x != A.y
|
||||
assert A.y != A.z
|
||||
assert A.z != A.x
|
||||
|
||||
assert A.x + 0 == A.x
|
||||
|
||||
v1 = x*A.x + y*A.y + z*A.z
|
||||
v2 = x**2*A.x + y**2*A.y + z**2*A.z
|
||||
v3 = v1 + v2
|
||||
v4 = v1 - v2
|
||||
|
||||
assert isinstance(v1, Vector)
|
||||
assert dot(v1, A.x) == x
|
||||
assert dot(v1, A.y) == y
|
||||
assert dot(v1, A.z) == z
|
||||
|
||||
assert isinstance(v2, Vector)
|
||||
assert dot(v2, A.x) == x**2
|
||||
assert dot(v2, A.y) == y**2
|
||||
assert dot(v2, A.z) == z**2
|
||||
|
||||
assert isinstance(v3, Vector)
|
||||
# We probably shouldn't be using simplify in dot...
|
||||
assert dot(v3, A.x) == x**2 + x
|
||||
assert dot(v3, A.y) == y**2 + y
|
||||
assert dot(v3, A.z) == z**2 + z
|
||||
|
||||
assert isinstance(v4, Vector)
|
||||
# We probably shouldn't be using simplify in dot...
|
||||
assert dot(v4, A.x) == x - x**2
|
||||
assert dot(v4, A.y) == y - y**2
|
||||
assert dot(v4, A.z) == z - z**2
|
||||
|
||||
assert v1.to_matrix(A) == Matrix([[x], [y], [z]])
|
||||
q = symbols('q')
|
||||
B = A.orientnew('B', 'Axis', (q, A.x))
|
||||
assert v1.to_matrix(B) == Matrix([[x],
|
||||
[ y * cos(q) + z * sin(q)],
|
||||
[-y * sin(q) + z * cos(q)]])
|
||||
|
||||
#Test the separate method
|
||||
B = ReferenceFrame('B')
|
||||
v5 = x*A.x + y*A.y + z*B.z
|
||||
assert Vector(0).separate() == {}
|
||||
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}
|
||||
|
||||
raises(TypeError, lambda: v3.applyfunc(v1))
|
||||
|
||||
|
||||
def test_Vector_diffs():
|
||||
q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4')
|
||||
q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1)
|
||||
q1dd, q2dd, q3dd, q4dd = dynamicsymbols('q1 q2 q3 q4', 2)
|
||||
N = ReferenceFrame('N')
|
||||
A = N.orientnew('A', 'Axis', [q3, N.z])
|
||||
B = A.orientnew('B', 'Axis', [q2, A.x])
|
||||
v1 = q2 * A.x + q3 * N.y
|
||||
v2 = q3 * B.x + v1
|
||||
v3 = v1.dt(B)
|
||||
v4 = v2.dt(B)
|
||||
v5 = q1*A.x + q2*A.y + q3*A.z
|
||||
|
||||
assert v1.dt(N) == q2d * A.x + q2 * q3d * A.y + q3d * N.y
|
||||
assert v1.dt(A) == q2d * A.x + q3 * q3d * N.x + q3d * N.y
|
||||
assert v1.dt(B) == (q2d * A.x + q3 * q3d * N.x + q3d *
|
||||
N.y - q3 * cos(q3) * q2d * N.z)
|
||||
assert v2.dt(N) == (q2d * A.x + (q2 + q3) * q3d * A.y + q3d * B.x + q3d *
|
||||
N.y)
|
||||
assert v2.dt(A) == q2d * A.x + q3d * B.x + q3 * q3d * N.x + q3d * N.y
|
||||
assert v2.dt(B) == (q2d * A.x + q3d * B.x + q3 * q3d * N.x + q3d * N.y -
|
||||
q3 * cos(q3) * q2d * N.z)
|
||||
assert v3.dt(N) == (q2dd * A.x + q2d * q3d * A.y + (q3d**2 + q3 * q3dd) *
|
||||
N.x + q3dd * N.y + (q3 * sin(q3) * q2d * q3d -
|
||||
cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z)
|
||||
assert v3.dt(A) == (q2dd * A.x + (2 * q3d**2 + q3 * q3dd) * N.x + (q3dd -
|
||||
q3 * q3d**2) * N.y + (q3 * sin(q3) * q2d * q3d -
|
||||
cos(q3) * q2d * q3d - q3 * cos(q3) * q2dd) * N.z)
|
||||
assert (v3.dt(B) - (q2dd*A.x - q3*cos(q3)*q2d**2*A.y + (2*q3d**2 +
|
||||
q3*q3dd)*N.x + (q3dd - q3*q3d**2)*N.y + (2*q3*sin(q3)*q2d*q3d -
|
||||
2*cos(q3)*q2d*q3d - q3*cos(q3)*q2dd)*N.z)).express(B).simplify() == 0
|
||||
assert v4.dt(N) == (q2dd * A.x + q3d * (q2d + q3d) * A.y + q3dd * B.x +
|
||||
(q3d**2 + q3 * q3dd) * N.x + q3dd * N.y + (q3 *
|
||||
sin(q3) * q2d * q3d - cos(q3) * q2d * q3d - q3 *
|
||||
cos(q3) * q2dd) * N.z)
|
||||
assert v4.dt(A) == (q2dd * A.x + q3dd * B.x + (2 * q3d**2 + q3 * q3dd) *
|
||||
N.x + (q3dd - q3 * q3d**2) * N.y + (q3 * sin(q3) *
|
||||
q2d * q3d - cos(q3) * q2d * q3d - q3 * cos(q3) *
|
||||
q2dd) * N.z)
|
||||
assert (v4.dt(B) - (q2dd*A.x - q3*cos(q3)*q2d**2*A.y + q3dd*B.x +
|
||||
(2*q3d**2 + q3*q3dd)*N.x + (q3dd - q3*q3d**2)*N.y +
|
||||
(2*q3*sin(q3)*q2d*q3d - 2*cos(q3)*q2d*q3d -
|
||||
q3*cos(q3)*q2dd)*N.z)).express(B).simplify() == 0
|
||||
assert v5.dt(B) == q1d*A.x + (q3*q2d + q2d)*A.y + (-q2*q2d + q3d)*A.z
|
||||
assert v5.dt(A) == q1d*A.x + q2d*A.y + q3d*A.z
|
||||
assert v5.dt(N) == (-q2*q3d + q1d)*A.x + (q1*q3d + q2d)*A.y + q3d*A.z
|
||||
assert v3.diff(q1d, N) == 0
|
||||
assert v3.diff(q2d, N) == A.x - q3 * cos(q3) * N.z
|
||||
assert v3.diff(q3d, N) == q3 * N.x + N.y
|
||||
assert v3.diff(q1d, A) == 0
|
||||
assert v3.diff(q2d, A) == A.x - q3 * cos(q3) * N.z
|
||||
assert v3.diff(q3d, A) == q3 * N.x + N.y
|
||||
assert v3.diff(q1d, B) == 0
|
||||
assert v3.diff(q2d, B) == A.x - q3 * cos(q3) * N.z
|
||||
assert v3.diff(q3d, B) == q3 * N.x + N.y
|
||||
assert v4.diff(q1d, N) == 0
|
||||
assert v4.diff(q2d, N) == A.x - q3 * cos(q3) * N.z
|
||||
assert v4.diff(q3d, N) == B.x + q3 * N.x + N.y
|
||||
assert v4.diff(q1d, A) == 0
|
||||
assert v4.diff(q2d, A) == A.x - q3 * cos(q3) * N.z
|
||||
assert v4.diff(q3d, A) == B.x + q3 * N.x + N.y
|
||||
assert v4.diff(q1d, B) == 0
|
||||
assert v4.diff(q2d, B) == A.x - q3 * cos(q3) * N.z
|
||||
assert v4.diff(q3d, B) == B.x + q3 * N.x + N.y
|
||||
|
||||
# diff() should only express vector components in the derivative frame if
|
||||
# the orientation of the component's frame depends on the variable
|
||||
v6 = q2**2*N.y + q2**2*A.y + q2**2*B.y
|
||||
# already expressed in N
|
||||
n_measy = 2*q2
|
||||
# A_C_N does not depend on q2, so don't express in N
|
||||
a_measy = 2*q2
|
||||
# B_C_N depends on q2, so express in N
|
||||
b_measx = (q2**2*B.y).dot(N.x).diff(q2)
|
||||
b_measy = (q2**2*B.y).dot(N.y).diff(q2)
|
||||
b_measz = (q2**2*B.y).dot(N.z).diff(q2)
|
||||
n_comp, a_comp = v6.diff(q2, N).args
|
||||
assert len(v6.diff(q2, N).args) == 2 # only N and A parts
|
||||
assert n_comp[1] == N
|
||||
assert a_comp[1] == A
|
||||
assert n_comp[0] == Matrix([b_measx, b_measy + n_measy, b_measz])
|
||||
assert a_comp[0] == Matrix([0, a_measy, 0])
|
||||
|
||||
|
||||
def test_vector_var_in_dcm():
|
||||
|
||||
N = ReferenceFrame('N')
|
||||
A = ReferenceFrame('A')
|
||||
B = ReferenceFrame('B')
|
||||
u1, u2, u3, u4 = dynamicsymbols('u1 u2 u3 u4')
|
||||
|
||||
v = u1 * u2 * A.x + u3 * N.y + u4**2 * N.z
|
||||
|
||||
assert v.diff(u1, N, var_in_dcm=False) == u2 * A.x
|
||||
assert v.diff(u1, A, var_in_dcm=False) == u2 * A.x
|
||||
assert v.diff(u3, N, var_in_dcm=False) == N.y
|
||||
assert v.diff(u3, A, var_in_dcm=False) == N.y
|
||||
assert v.diff(u3, B, var_in_dcm=False) == N.y
|
||||
assert v.diff(u4, N, var_in_dcm=False) == 2 * u4 * N.z
|
||||
|
||||
raises(ValueError, lambda: v.diff(u1, N))
|
||||
|
||||
|
||||
def test_vector_simplify():
|
||||
x, y, z, k, n, m, w, f, s, A = symbols('x, y, z, k, n, m, w, f, s, A')
|
||||
N = ReferenceFrame('N')
|
||||
|
||||
test1 = (1 / x + 1 / y) * N.x
|
||||
assert (test1 & N.x) != (x + y) / (x * y)
|
||||
test1 = test1.simplify()
|
||||
assert (test1 & N.x) == (x + y) / (x * y)
|
||||
|
||||
test2 = (A**2 * s**4 / (4 * pi * k * m**3)) * N.x
|
||||
test2 = test2.simplify()
|
||||
assert (test2 & N.x) == (A**2 * s**4 / (4 * pi * k * m**3))
|
||||
|
||||
test3 = ((4 + 4 * x - 2 * (2 + 2 * x)) / (2 + 2 * x)) * N.x
|
||||
test3 = test3.simplify()
|
||||
assert (test3 & N.x) == 0
|
||||
|
||||
test4 = ((-4 * x * y**2 - 2 * y**3 - 2 * x**2 * y) / (x + y)**2) * N.x
|
||||
test4 = test4.simplify()
|
||||
assert (test4 & N.x) == -2 * y
|
||||
|
||||
|
||||
def test_vector_evalf():
|
||||
a, b = symbols('a b')
|
||||
v = pi * A.x
|
||||
assert v.evalf(2) == Float('3.1416', 2) * A.x
|
||||
v = pi * A.x + 5 * a * A.y - b * A.z
|
||||
assert v.evalf(3) == Float('3.1416', 3) * A.x + Float('5', 3) * a * A.y - b * A.z
|
||||
assert v.evalf(5, subs={a: 1.234, b:5.8973}) == Float('3.1415926536', 5) * A.x + Float('6.17', 5) * A.y - Float('5.8973', 5) * A.z
|
||||
|
||||
|
||||
def test_vector_angle():
|
||||
A = ReferenceFrame('A')
|
||||
v1 = A.x + A.y
|
||||
v2 = A.z
|
||||
assert v1.angle_between(v2) == pi/2
|
||||
B = ReferenceFrame('B')
|
||||
B.orient_axis(A, A.x, pi)
|
||||
v3 = A.x
|
||||
v4 = B.x
|
||||
assert v3.angle_between(v4) == 0
|
||||
|
||||
|
||||
def test_vector_xreplace():
|
||||
x, y, z = symbols('x y z')
|
||||
v = x**2 * A.x + x*y * A.y + x*y*z * A.z
|
||||
assert v.xreplace({x : cos(x)}) == cos(x)**2 * A.x + y*cos(x) * A.y + y*z*cos(x) * A.z
|
||||
assert v.xreplace({x*y : pi}) == x**2 * A.x + pi * A.y + x*y*z * A.z
|
||||
assert v.xreplace({x*y*z : 1}) == x**2*A.x + x*y*A.y + A.z
|
||||
assert v.xreplace({x:1, z:0}) == A.x + y * A.y
|
||||
raises(TypeError, lambda: v.xreplace())
|
||||
raises(TypeError, lambda: v.xreplace([x, y]))
|
||||
|
||||
def test_issue_23366():
|
||||
u1 = dynamicsymbols('u1')
|
||||
N = ReferenceFrame('N')
|
||||
N_v_A = u1*N.x
|
||||
raises(VectorTypeError, lambda: N_v_A.diff(N, u1))
|
||||
|
||||
|
||||
def test_vector_outer():
|
||||
a, b, c, d, e, f = symbols('a, b, c, d, e, f')
|
||||
N = ReferenceFrame('N')
|
||||
v1 = a*N.x + b*N.y + c*N.z
|
||||
v2 = d*N.x + e*N.y + f*N.z
|
||||
v1v2 = Matrix([[a*d, a*e, a*f],
|
||||
[b*d, b*e, b*f],
|
||||
[c*d, c*e, c*f]])
|
||||
assert v1.outer(v2).to_matrix(N) == v1v2
|
||||
assert (v1 | v2).to_matrix(N) == v1v2
|
||||
v2v1 = Matrix([[d*a, d*b, d*c],
|
||||
[e*a, e*b, e*c],
|
||||
[f*a, f*b, f*c]])
|
||||
assert v2.outer(v1).to_matrix(N) == v2v1
|
||||
assert (v2 | v1).to_matrix(N) == v2v1
|
||||
|
||||
|
||||
def test_overloaded_operators():
|
||||
a, b, c, d, e, f = symbols('a, b, c, d, e, f')
|
||||
N = ReferenceFrame('N')
|
||||
v1 = a*N.x + b*N.y + c*N.z
|
||||
v2 = d*N.x + e*N.y + f*N.z
|
||||
|
||||
assert v1 + v2 == v2 + v1
|
||||
assert v1 - v2 == -v2 + v1
|
||||
assert v1 & v2 == v2 & v1
|
||||
assert v1 ^ v2 == v1.cross(v2)
|
||||
assert v2 ^ v1 == v2.cross(v1)
|
||||
@@ -0,0 +1,806 @@
|
||||
from sympy import (S, sympify, expand, sqrt, Add, zeros, acos,
|
||||
ImmutableMatrix as Matrix, simplify)
|
||||
from sympy.simplify.trigsimp import trigsimp
|
||||
from sympy.printing.defaults import Printable
|
||||
from sympy.utilities.misc import filldedent
|
||||
from sympy.core.evalf import EvalfMixin
|
||||
|
||||
from mpmath.libmp.libmpf import prec_to_dps
|
||||
|
||||
|
||||
__all__ = ['Vector']
|
||||
|
||||
|
||||
class Vector(Printable, EvalfMixin):
|
||||
"""The class used to define vectors.
|
||||
|
||||
It along with ReferenceFrame are the building blocks of describing a
|
||||
classical mechanics system in PyDy and sympy.physics.vector.
|
||||
|
||||
Attributes
|
||||
==========
|
||||
|
||||
simp : Boolean
|
||||
Let certain methods use trigsimp on their outputs
|
||||
|
||||
"""
|
||||
|
||||
simp = False
|
||||
is_number = False
|
||||
|
||||
def __init__(self, inlist):
|
||||
"""This is the constructor for the Vector class. You should not be
|
||||
calling this, it should only be used by other functions. You should be
|
||||
treating Vectors like you would with if you were doing the math by
|
||||
hand, and getting the first 3 from the standard basis vectors from a
|
||||
ReferenceFrame.
|
||||
|
||||
The only exception is to create a zero vector:
|
||||
zv = Vector(0)
|
||||
|
||||
"""
|
||||
|
||||
self.args = []
|
||||
if inlist == 0:
|
||||
inlist = []
|
||||
if isinstance(inlist, dict):
|
||||
d = inlist
|
||||
else:
|
||||
d = {}
|
||||
for inp in inlist:
|
||||
if inp[1] in d:
|
||||
d[inp[1]] += inp[0]
|
||||
else:
|
||||
d[inp[1]] = inp[0]
|
||||
|
||||
for k, v in d.items():
|
||||
if v != Matrix([0, 0, 0]):
|
||||
self.args.append((v, k))
|
||||
|
||||
@property
|
||||
def func(self):
|
||||
"""Returns the class Vector. """
|
||||
return Vector
|
||||
|
||||
def __hash__(self):
|
||||
return hash(tuple(self.args))
|
||||
|
||||
def __add__(self, other):
|
||||
"""The add operator for Vector. """
|
||||
if other == 0:
|
||||
return self
|
||||
other = _check_vector(other)
|
||||
return Vector(self.args + other.args)
|
||||
|
||||
def dot(self, other):
|
||||
"""Dot product of two vectors.
|
||||
|
||||
Returns a scalar, the dot product of the two Vectors
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other : Vector
|
||||
The Vector which we are dotting with
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, dot
|
||||
>>> from sympy import symbols
|
||||
>>> q1 = symbols('q1')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> dot(N.x, N.x)
|
||||
1
|
||||
>>> dot(N.x, N.y)
|
||||
0
|
||||
>>> A = N.orientnew('A', 'Axis', [q1, N.x])
|
||||
>>> dot(N.y, A.y)
|
||||
cos(q1)
|
||||
|
||||
"""
|
||||
|
||||
from sympy.physics.vector.dyadic import Dyadic, _check_dyadic
|
||||
if isinstance(other, Dyadic):
|
||||
other = _check_dyadic(other)
|
||||
ol = Vector(0)
|
||||
for v in other.args:
|
||||
ol += v[0] * v[2] * (v[1].dot(self))
|
||||
return ol
|
||||
other = _check_vector(other)
|
||||
out = S.Zero
|
||||
for v1 in self.args:
|
||||
for v2 in other.args:
|
||||
out += ((v2[0].T) * (v2[1].dcm(v1[1])) * (v1[0]))[0]
|
||||
if Vector.simp:
|
||||
return trigsimp(out, recursive=True)
|
||||
else:
|
||||
return out
|
||||
|
||||
def __truediv__(self, other):
|
||||
"""This uses mul and inputs self and 1 divided by other. """
|
||||
return self.__mul__(S.One / other)
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Tests for equality.
|
||||
|
||||
It is very import to note that this is only as good as the SymPy
|
||||
equality test; False does not always mean they are not equivalent
|
||||
Vectors.
|
||||
If other is 0, and self is empty, returns True.
|
||||
If other is 0 and self is not empty, returns False.
|
||||
If none of the above, only accepts other as a Vector.
|
||||
|
||||
"""
|
||||
|
||||
if other == 0:
|
||||
other = Vector(0)
|
||||
try:
|
||||
other = _check_vector(other)
|
||||
except TypeError:
|
||||
return False
|
||||
if (self.args == []) and (other.args == []):
|
||||
return True
|
||||
elif (self.args == []) or (other.args == []):
|
||||
return False
|
||||
|
||||
frame = self.args[0][1]
|
||||
for v in frame:
|
||||
if expand((self - other).dot(v)) != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def __mul__(self, other):
|
||||
"""Multiplies the Vector by a sympifyable expression.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other : Sympifyable
|
||||
The scalar to multiply this Vector with
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy import Symbol
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> b = Symbol('b')
|
||||
>>> V = 10 * b * N.x
|
||||
>>> print(V)
|
||||
10*b*N.x
|
||||
|
||||
"""
|
||||
|
||||
newlist = list(self.args)
|
||||
other = sympify(other)
|
||||
for i in range(len(newlist)):
|
||||
newlist[i] = (other * newlist[i][0], newlist[i][1])
|
||||
return Vector(newlist)
|
||||
|
||||
def __neg__(self):
|
||||
return self * -1
|
||||
|
||||
def outer(self, other):
|
||||
"""Outer product between two Vectors.
|
||||
|
||||
A rank increasing operation, which returns a Dyadic from two Vectors
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other : Vector
|
||||
The Vector to take the outer product with
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, outer
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> outer(N.x, N.x)
|
||||
(N.x|N.x)
|
||||
|
||||
"""
|
||||
|
||||
from sympy.physics.vector.dyadic import Dyadic
|
||||
other = _check_vector(other)
|
||||
ol = Dyadic(0)
|
||||
for v in self.args:
|
||||
for v2 in other.args:
|
||||
# it looks this way because if we are in the same frame and
|
||||
# use the enumerate function on the same frame in a nested
|
||||
# fashion, then bad things happen
|
||||
ol += Dyadic([(v[0][0] * v2[0][0], v[1].x, v2[1].x)])
|
||||
ol += Dyadic([(v[0][0] * v2[0][1], v[1].x, v2[1].y)])
|
||||
ol += Dyadic([(v[0][0] * v2[0][2], v[1].x, v2[1].z)])
|
||||
ol += Dyadic([(v[0][1] * v2[0][0], v[1].y, v2[1].x)])
|
||||
ol += Dyadic([(v[0][1] * v2[0][1], v[1].y, v2[1].y)])
|
||||
ol += Dyadic([(v[0][1] * v2[0][2], v[1].y, v2[1].z)])
|
||||
ol += Dyadic([(v[0][2] * v2[0][0], v[1].z, v2[1].x)])
|
||||
ol += Dyadic([(v[0][2] * v2[0][1], v[1].z, v2[1].y)])
|
||||
ol += Dyadic([(v[0][2] * v2[0][2], v[1].z, v2[1].z)])
|
||||
return ol
|
||||
|
||||
def _latex(self, printer):
|
||||
"""Latex Printing method. """
|
||||
|
||||
ar = self.args # just to shorten things
|
||||
if len(ar) == 0:
|
||||
return str(0)
|
||||
ol = [] # output list, to be concatenated to a string
|
||||
for v in ar:
|
||||
for j in 0, 1, 2:
|
||||
# if the coef of the basis vector is 1, we skip the 1
|
||||
if v[0][j] == 1:
|
||||
ol.append(' + ' + v[1].latex_vecs[j])
|
||||
# if the coef of the basis vector is -1, we skip the 1
|
||||
elif v[0][j] == -1:
|
||||
ol.append(' - ' + v[1].latex_vecs[j])
|
||||
elif v[0][j] != 0:
|
||||
# If the coefficient of the basis vector is not 1 or -1;
|
||||
# also, we might wrap it in parentheses, for readability.
|
||||
arg_str = printer._print(v[0][j])
|
||||
if isinstance(v[0][j], Add):
|
||||
arg_str = "(%s)" % arg_str
|
||||
if arg_str[0] == '-':
|
||||
arg_str = arg_str[1:]
|
||||
str_start = ' - '
|
||||
else:
|
||||
str_start = ' + '
|
||||
ol.append(str_start + arg_str + v[1].latex_vecs[j])
|
||||
outstr = ''.join(ol)
|
||||
if outstr.startswith(' + '):
|
||||
outstr = outstr[3:]
|
||||
elif outstr.startswith(' '):
|
||||
outstr = outstr[1:]
|
||||
return outstr
|
||||
|
||||
def _pretty(self, printer):
|
||||
"""Pretty Printing method. """
|
||||
from sympy.printing.pretty.stringpict import prettyForm
|
||||
|
||||
terms = []
|
||||
|
||||
def juxtapose(a, b):
|
||||
pa = printer._print(a)
|
||||
pb = printer._print(b)
|
||||
if a.is_Add:
|
||||
pa = prettyForm(*pa.parens())
|
||||
return printer._print_seq([pa, pb], delimiter=' ')
|
||||
|
||||
for M, N in self.args:
|
||||
for i in range(3):
|
||||
if M[i] == 0:
|
||||
continue
|
||||
elif M[i] == 1:
|
||||
terms.append(prettyForm(N.pretty_vecs[i]))
|
||||
elif M[i] == -1:
|
||||
terms.append(prettyForm("-1") * prettyForm(N.pretty_vecs[i]))
|
||||
else:
|
||||
terms.append(juxtapose(M[i], N.pretty_vecs[i]))
|
||||
|
||||
if terms:
|
||||
pretty_result = prettyForm.__add__(*terms)
|
||||
else:
|
||||
pretty_result = prettyForm("0")
|
||||
|
||||
return pretty_result
|
||||
|
||||
def __rsub__(self, other):
|
||||
return (-1 * self) + other
|
||||
|
||||
def _sympystr(self, printer, order=True):
|
||||
"""Printing method. """
|
||||
if not order or len(self.args) == 1:
|
||||
ar = list(self.args)
|
||||
elif len(self.args) == 0:
|
||||
return printer._print(0)
|
||||
else:
|
||||
d = {v[1]: v[0] for v in self.args}
|
||||
keys = sorted(d.keys(), key=lambda x: x.index)
|
||||
ar = []
|
||||
for key in keys:
|
||||
ar.append((d[key], key))
|
||||
ol = [] # output list, to be concatenated to a string
|
||||
for v in ar:
|
||||
for j in 0, 1, 2:
|
||||
# if the coef of the basis vector is 1, we skip the 1
|
||||
if v[0][j] == 1:
|
||||
ol.append(' + ' + v[1].str_vecs[j])
|
||||
# if the coef of the basis vector is -1, we skip the 1
|
||||
elif v[0][j] == -1:
|
||||
ol.append(' - ' + v[1].str_vecs[j])
|
||||
elif v[0][j] != 0:
|
||||
# If the coefficient of the basis vector is not 1 or -1;
|
||||
# also, we might wrap it in parentheses, for readability.
|
||||
arg_str = printer._print(v[0][j])
|
||||
if isinstance(v[0][j], Add):
|
||||
arg_str = "(%s)" % arg_str
|
||||
if arg_str[0] == '-':
|
||||
arg_str = arg_str[1:]
|
||||
str_start = ' - '
|
||||
else:
|
||||
str_start = ' + '
|
||||
ol.append(str_start + arg_str + '*' + v[1].str_vecs[j])
|
||||
outstr = ''.join(ol)
|
||||
if outstr.startswith(' + '):
|
||||
outstr = outstr[3:]
|
||||
elif outstr.startswith(' '):
|
||||
outstr = outstr[1:]
|
||||
return outstr
|
||||
|
||||
def __sub__(self, other):
|
||||
"""The subtraction operator. """
|
||||
return self.__add__(other * -1)
|
||||
|
||||
def cross(self, other):
|
||||
"""The cross product operator for two Vectors.
|
||||
|
||||
Returns a Vector, expressed in the same ReferenceFrames as self.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other : Vector
|
||||
The Vector which we are crossing with
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.vector import ReferenceFrame, cross
|
||||
>>> q1 = symbols('q1')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> cross(N.x, N.y)
|
||||
N.z
|
||||
>>> A = ReferenceFrame('A')
|
||||
>>> A.orient_axis(N, q1, N.x)
|
||||
>>> cross(A.x, N.y)
|
||||
N.z
|
||||
>>> cross(N.y, A.x)
|
||||
- sin(q1)*A.y - cos(q1)*A.z
|
||||
|
||||
"""
|
||||
|
||||
from sympy.physics.vector.dyadic import Dyadic, _check_dyadic
|
||||
if isinstance(other, Dyadic):
|
||||
other = _check_dyadic(other)
|
||||
ol = Dyadic(0)
|
||||
for i, v in enumerate(other.args):
|
||||
ol += v[0] * ((self.cross(v[1])).outer(v[2]))
|
||||
return ol
|
||||
other = _check_vector(other)
|
||||
if other.args == []:
|
||||
return Vector(0)
|
||||
|
||||
def _det(mat):
|
||||
"""This is needed as a little method for to find the determinant
|
||||
of a list in python; needs to work for a 3x3 list.
|
||||
SymPy's Matrix will not take in Vector, so need a custom function.
|
||||
You should not be calling this.
|
||||
|
||||
"""
|
||||
|
||||
return (mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1])
|
||||
+ mat[0][1] * (mat[1][2] * mat[2][0] - mat[1][0] *
|
||||
mat[2][2]) + mat[0][2] * (mat[1][0] * mat[2][1] -
|
||||
mat[1][1] * mat[2][0]))
|
||||
|
||||
outlist = []
|
||||
ar = other.args # For brevity
|
||||
for v in ar:
|
||||
tempx = v[1].x
|
||||
tempy = v[1].y
|
||||
tempz = v[1].z
|
||||
tempm = ([[tempx, tempy, tempz],
|
||||
[self.dot(tempx), self.dot(tempy), self.dot(tempz)],
|
||||
[Vector([v]).dot(tempx), Vector([v]).dot(tempy),
|
||||
Vector([v]).dot(tempz)]])
|
||||
outlist += _det(tempm).args
|
||||
return Vector(outlist)
|
||||
|
||||
__radd__ = __add__
|
||||
__rmul__ = __mul__
|
||||
|
||||
def separate(self):
|
||||
"""
|
||||
The constituents of this vector in different reference frames,
|
||||
as per its definition.
|
||||
|
||||
Returns a dict mapping each ReferenceFrame to the corresponding
|
||||
constituent Vector.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> R1 = ReferenceFrame('R1')
|
||||
>>> R2 = ReferenceFrame('R2')
|
||||
>>> v = R1.x + R2.x
|
||||
>>> v.separate() == {R1: R1.x, R2: R2.x}
|
||||
True
|
||||
|
||||
"""
|
||||
|
||||
components = {}
|
||||
for x in self.args:
|
||||
components[x[1]] = Vector([x])
|
||||
return components
|
||||
|
||||
def __and__(self, other):
|
||||
return self.dot(other)
|
||||
__and__.__doc__ = dot.__doc__
|
||||
__rand__ = __and__
|
||||
|
||||
def __xor__(self, other):
|
||||
return self.cross(other)
|
||||
__xor__.__doc__ = cross.__doc__
|
||||
|
||||
def __or__(self, other):
|
||||
return self.outer(other)
|
||||
__or__.__doc__ = outer.__doc__
|
||||
|
||||
def diff(self, var, frame, var_in_dcm=True):
|
||||
"""Returns the partial derivative of the vector with respect to a
|
||||
variable in the provided reference frame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
var : Symbol
|
||||
What the partial derivative is taken with respect to.
|
||||
frame : ReferenceFrame
|
||||
The reference frame that the partial derivative is taken in.
|
||||
var_in_dcm : boolean
|
||||
If true, the differentiation algorithm assumes that the variable
|
||||
may be present in any of the direction cosine matrices that relate
|
||||
the frame to the frames of any component of the vector. But if it
|
||||
is known that the variable is not present in the direction cosine
|
||||
matrices, false can be set to skip full reexpression in the desired
|
||||
frame.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Symbol
|
||||
>>> from sympy.physics.vector import dynamicsymbols, ReferenceFrame
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> t = Symbol('t')
|
||||
>>> q1 = dynamicsymbols('q1')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> A = N.orientnew('A', 'Axis', [q1, N.y])
|
||||
>>> A.x.diff(t, N)
|
||||
- sin(q1)*q1'*N.x - cos(q1)*q1'*N.z
|
||||
>>> A.x.diff(t, N).express(A).simplify()
|
||||
- q1'*A.z
|
||||
>>> B = ReferenceFrame('B')
|
||||
>>> u1, u2 = dynamicsymbols('u1, u2')
|
||||
>>> v = u1 * A.x + u2 * B.y
|
||||
>>> v.diff(u2, N, var_in_dcm=False)
|
||||
B.y
|
||||
|
||||
"""
|
||||
|
||||
from sympy.physics.vector.frame import _check_frame
|
||||
|
||||
_check_frame(frame)
|
||||
var = sympify(var)
|
||||
|
||||
inlist = []
|
||||
|
||||
for vector_component in self.args:
|
||||
measure_number = vector_component[0]
|
||||
component_frame = vector_component[1]
|
||||
if component_frame == frame:
|
||||
inlist += [(measure_number.diff(var), frame)]
|
||||
else:
|
||||
# If the direction cosine matrix relating the component frame
|
||||
# with the derivative frame does not contain the variable.
|
||||
if not var_in_dcm or (frame.dcm(component_frame).diff(var) ==
|
||||
zeros(3, 3)):
|
||||
inlist += [(measure_number.diff(var), component_frame)]
|
||||
else: # else express in the frame
|
||||
reexp_vec_comp = Vector([vector_component]).express(frame)
|
||||
deriv = reexp_vec_comp.args[0][0].diff(var)
|
||||
inlist += Vector([(deriv, frame)]).args
|
||||
|
||||
return Vector(inlist)
|
||||
|
||||
def express(self, otherframe, variables=False):
|
||||
"""
|
||||
Returns a Vector equivalent to this one, expressed in otherframe.
|
||||
Uses the global express method.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
otherframe : ReferenceFrame
|
||||
The frame for this Vector to be described in
|
||||
|
||||
variables : boolean
|
||||
If True, the coordinate symbols(if present) in this Vector
|
||||
are re-expressed in terms otherframe
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
|
||||
>>> from sympy.physics.vector import init_vprinting
|
||||
>>> init_vprinting(pretty_print=False)
|
||||
>>> q1 = dynamicsymbols('q1')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> A = N.orientnew('A', 'Axis', [q1, N.y])
|
||||
>>> A.x.express(N)
|
||||
cos(q1)*N.x - sin(q1)*N.z
|
||||
|
||||
"""
|
||||
from sympy.physics.vector import express
|
||||
return express(self, otherframe, variables=variables)
|
||||
|
||||
def to_matrix(self, reference_frame):
|
||||
"""Returns the matrix form of the vector with respect to the given
|
||||
frame.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
reference_frame : ReferenceFrame
|
||||
The reference frame that the rows of the matrix correspond to.
|
||||
|
||||
Returns
|
||||
-------
|
||||
matrix : ImmutableMatrix, shape(3,1)
|
||||
The matrix that gives the 1D vector.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> a, b, c = symbols('a, b, c')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> vector = a * N.x + b * N.y + c * N.z
|
||||
>>> vector.to_matrix(N)
|
||||
Matrix([
|
||||
[a],
|
||||
[b],
|
||||
[c]])
|
||||
>>> beta = symbols('beta')
|
||||
>>> A = N.orientnew('A', 'Axis', (beta, N.x))
|
||||
>>> vector.to_matrix(A)
|
||||
Matrix([
|
||||
[ a],
|
||||
[ b*cos(beta) + c*sin(beta)],
|
||||
[-b*sin(beta) + c*cos(beta)]])
|
||||
|
||||
"""
|
||||
|
||||
return Matrix([self.dot(unit_vec) for unit_vec in
|
||||
reference_frame]).reshape(3, 1)
|
||||
|
||||
def doit(self, **hints):
|
||||
"""Calls .doit() on each term in the Vector"""
|
||||
d = {}
|
||||
for v in self.args:
|
||||
d[v[1]] = v[0].applyfunc(lambda x: x.doit(**hints))
|
||||
return Vector(d)
|
||||
|
||||
def dt(self, otherframe):
|
||||
"""
|
||||
Returns a Vector which is the time derivative of
|
||||
the self Vector, taken in frame otherframe.
|
||||
|
||||
Calls the global time_derivative method
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
otherframe : ReferenceFrame
|
||||
The frame to calculate the time derivative in
|
||||
|
||||
"""
|
||||
from sympy.physics.vector import time_derivative
|
||||
return time_derivative(self, otherframe)
|
||||
|
||||
def simplify(self):
|
||||
"""Returns a simplified Vector."""
|
||||
d = {}
|
||||
for v in self.args:
|
||||
d[v[1]] = simplify(v[0])
|
||||
return Vector(d)
|
||||
|
||||
def subs(self, *args, **kwargs):
|
||||
"""Substitution on the Vector.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> from sympy import Symbol
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> s = Symbol('s')
|
||||
>>> a = N.x * s
|
||||
>>> a.subs({s: 2})
|
||||
2*N.x
|
||||
|
||||
"""
|
||||
|
||||
d = {}
|
||||
for v in self.args:
|
||||
d[v[1]] = v[0].subs(*args, **kwargs)
|
||||
return Vector(d)
|
||||
|
||||
def magnitude(self):
|
||||
"""Returns the magnitude (Euclidean norm) of self.
|
||||
|
||||
Warnings
|
||||
========
|
||||
|
||||
Python ignores the leading negative sign so that might
|
||||
give wrong results.
|
||||
``-A.x.magnitude()`` would be treated as ``-(A.x.magnitude())``,
|
||||
instead of ``(-A.x).magnitude()``.
|
||||
|
||||
"""
|
||||
return sqrt(self.dot(self))
|
||||
|
||||
def normalize(self):
|
||||
"""Returns a Vector of magnitude 1, codirectional with self."""
|
||||
return Vector(self.args + []) / self.magnitude()
|
||||
|
||||
def applyfunc(self, f):
|
||||
"""Apply a function to each component of a vector."""
|
||||
if not callable(f):
|
||||
raise TypeError("`f` must be callable.")
|
||||
|
||||
d = {}
|
||||
for v in self.args:
|
||||
d[v[1]] = v[0].applyfunc(f)
|
||||
return Vector(d)
|
||||
|
||||
def angle_between(self, vec):
|
||||
"""
|
||||
Returns the smallest angle between Vector 'vec' and self.
|
||||
|
||||
Parameter
|
||||
=========
|
||||
|
||||
vec : Vector
|
||||
The Vector between which angle is needed.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> A = ReferenceFrame("A")
|
||||
>>> v1 = A.x
|
||||
>>> v2 = A.y
|
||||
>>> v1.angle_between(v2)
|
||||
pi/2
|
||||
|
||||
>>> v3 = A.x + A.y + A.z
|
||||
>>> v1.angle_between(v3)
|
||||
acos(sqrt(3)/3)
|
||||
|
||||
Warnings
|
||||
========
|
||||
|
||||
Python ignores the leading negative sign so that might give wrong
|
||||
results. ``-A.x.angle_between()`` would be treated as
|
||||
``-(A.x.angle_between())``, instead of ``(-A.x).angle_between()``.
|
||||
|
||||
"""
|
||||
|
||||
vec1 = self.normalize()
|
||||
vec2 = vec.normalize()
|
||||
angle = acos(vec1.dot(vec2))
|
||||
return angle
|
||||
|
||||
def free_symbols(self, reference_frame):
|
||||
"""Returns the free symbols in the measure numbers of the vector
|
||||
expressed in the given reference frame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
reference_frame : ReferenceFrame
|
||||
The frame with respect to which the free symbols of the given
|
||||
vector is to be determined.
|
||||
|
||||
Returns
|
||||
=======
|
||||
set of Symbol
|
||||
set of symbols present in the measure numbers of
|
||||
``reference_frame``.
|
||||
|
||||
"""
|
||||
|
||||
return self.to_matrix(reference_frame).free_symbols
|
||||
|
||||
def free_dynamicsymbols(self, reference_frame):
|
||||
"""Returns the free dynamic symbols (functions of time ``t``) in the
|
||||
measure numbers of the vector expressed in the given reference frame.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
reference_frame : ReferenceFrame
|
||||
The frame with respect to which the free dynamic symbols of the
|
||||
given vector is to be determined.
|
||||
|
||||
Returns
|
||||
=======
|
||||
set
|
||||
Set of functions of time ``t``, e.g.
|
||||
``Function('f')(me.dynamicsymbols._t)``.
|
||||
|
||||
"""
|
||||
# TODO : Circular dependency if imported at top. Should move
|
||||
# find_dynamicsymbols into physics.vector.functions.
|
||||
from sympy.physics.mechanics.functions import find_dynamicsymbols
|
||||
|
||||
return find_dynamicsymbols(self, reference_frame=reference_frame)
|
||||
|
||||
def _eval_evalf(self, prec):
|
||||
if not self.args:
|
||||
return self
|
||||
new_args = []
|
||||
dps = prec_to_dps(prec)
|
||||
for mat, frame in self.args:
|
||||
new_args.append([mat.evalf(n=dps), frame])
|
||||
return Vector(new_args)
|
||||
|
||||
def xreplace(self, rule):
|
||||
"""Replace occurrences of objects within the measure numbers of the
|
||||
vector.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
rule : dict-like
|
||||
Expresses a replacement rule.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
Vector
|
||||
Result of the replacement.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, pi
|
||||
>>> from sympy.physics.vector import ReferenceFrame
|
||||
>>> A = ReferenceFrame('A')
|
||||
>>> x, y, z = symbols('x y z')
|
||||
>>> ((1 + x*y) * A.x).xreplace({x: pi})
|
||||
(pi*y + 1)*A.x
|
||||
>>> ((1 + x*y) * A.x).xreplace({x: pi, y: 2})
|
||||
(1 + 2*pi)*A.x
|
||||
|
||||
Replacements occur only if an entire node in the expression tree is
|
||||
matched:
|
||||
|
||||
>>> ((x*y + z) * A.x).xreplace({x*y: pi})
|
||||
(z + pi)*A.x
|
||||
>>> ((x*y*z) * A.x).xreplace({x*y: pi})
|
||||
x*y*z*A.x
|
||||
|
||||
"""
|
||||
|
||||
new_args = []
|
||||
for mat, frame in self.args:
|
||||
mat = mat.xreplace(rule)
|
||||
new_args.append([mat, frame])
|
||||
return Vector(new_args)
|
||||
|
||||
|
||||
class VectorTypeError(TypeError):
|
||||
|
||||
def __init__(self, other, want):
|
||||
msg = filldedent("Expected an instance of %s, but received object "
|
||||
"'%s' of %s." % (type(want), other, type(other)))
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
def _check_vector(other):
|
||||
if not isinstance(other, Vector):
|
||||
raise TypeError('A Vector must be supplied')
|
||||
return other
|
||||
Reference in New Issue
Block a user