# Source code for sympy.physics.vector.dyadic

from sympy import sympify, Add, ImmutableMatrix as Matrix
from sympy.core.compatibility import u, unicode
from .printing import (VectorLatexPrinter, VectorPrettyPrinter,
VectorStrPrinter)

__all__ = ['Dyadic']

[docs]class Dyadic(object):
"""A Dyadic object.

See:
http://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.

"""

def __init__(self, inlist):
"""
Just like Vector's init, you shouldn't 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

def __add__(self, other):
"""The add operator for Dyadic. """
other = _check_dyadic(other)
return Dyadic(self.args + other.args)

def __and__(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 i, v in enumerate(self.args):
for i2, v2 in enumerate(other.args):
ol += v[0] * v2[0] * (v[2] & v2[1]) * (v[1] | v2[2])
else:
other = _check_vector(other)
ol = Vector(0)
for i, v in enumerate(self.args):
ol += v[0] * v[1] * (v[2] & other)
return ol

def __div__(self, other):
"""Divides the Dyadic by a sympifyable expression. """
return self.__mul__(1 / other)

__truediv__ = __div__

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 __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 = [v for v in self.args]
for i, v in enumerate(newlist):
newlist[i] = (sympify(other) * newlist[i][0], newlist[i][1],
newlist[i][2])
return Dyadic(newlist)

def __ne__(self, other):
return not self.__eq__(other)

def __neg__(self):
return self * -1

def _latex(self, printer=None):
ar = self.args  # just to shorten things
if len(ar) == 0:
return str(0)
ol = []  # output list, to be concatenated to a string
mlp = VectorLatexPrinter()
for i, v in enumerate(ar):
# if the coef of the dyadic is 1, we skip the 1
if ar[i][0] == 1:
ol.append(' + ' + mlp.doprint(ar[i][1]) + r"\otimes " +
mlp.doprint(ar[i][2]))
# if the coef of the dyadic is -1, we skip the 1
elif ar[i][0] == -1:
ol.append(' - ' +
mlp.doprint(ar[i][1]) +
r"\otimes " +
mlp.doprint(ar[i][2]))
# If the coefficient of the dyadic is not 1 or -1,
# we might wrap it in parentheses, for readability.
elif ar[i][0] != 0:
arg_str = mlp.doprint(ar[i][0])
if isinstance(ar[i][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 + mlp.doprint(ar[i][1]) +
r"\otimes " + mlp.doprint(ar[i][2]))
outstr = ''.join(ol)
if outstr.startswith(' + '):
outstr = outstr[3:]
elif outstr.startswith(' '):
outstr = outstr[1:]
return outstr

def _pretty(self, printer=None):
e = self

class Fake(object):
baseline = 0

def render(self, *args, **kwargs):
ar = e.args  # just to shorten things
settings = printer._settings if printer else {}
if printer:
use_unicode = printer._use_unicode
else:
from sympy.printing.pretty.pretty_symbology import (
pretty_use_unicode)
use_unicode = pretty_use_unicode()
mpp = printer if printer else VectorPrettyPrinter(settings)
if len(ar) == 0:
return unicode(0)
bar = u("\N{CIRCLED TIMES}") if use_unicode else "|"
ol = []  # output list, to be concatenated to a string
for i, v in enumerate(ar):
# if the coef of the dyadic is 1, we skip the 1
if ar[i][0] == 1:
ol.extend([u(" + "),
mpp.doprint(ar[i][1]),
bar,
mpp.doprint(ar[i][2])])

# if the coef of the dyadic is -1, we skip the 1
elif ar[i][0] == -1:
ol.extend([u(" - "),
mpp.doprint(ar[i][1]),
bar,
mpp.doprint(ar[i][2])])

# If the coefficient of the dyadic is not 1 or -1,
# we might wrap it in parentheses, for readability.
elif ar[i][0] != 0:
if isinstance(ar[i][0], Add):
arg_str = mpp._print(
ar[i][0]).parens()[0]
else:
arg_str = mpp.doprint(ar[i][0])
if arg_str.startswith(u("-")):
arg_str = arg_str[1:]
str_start = u(" - ")
else:
str_start = u(" + ")
ol.extend([str_start, arg_str, u(" "),
mpp.doprint(ar[i][1]),
bar,
mpp.doprint(ar[i][2])])

outstr = u("").join(ol)
if outstr.startswith(u(" + ")):
outstr = outstr[3:]
elif outstr.startswith(" "):
outstr = outstr[1:]
return outstr
return Fake()

def __rand__(self, other):
"""The inner product operator for a Vector or Dyadic, and a Dyadic

This is for: Vector dot Dyadic

Parameters
==========

other : Vector
The vector we are dotting with

Examples
========

>>> from sympy.physics.vector import ReferenceFrame, dot, outer
>>> N = ReferenceFrame('N')
>>> d = outer(N.x, N.x)
>>> dot(N.x, d)
N.x

"""

from sympy.physics.vector.vector import Vector, _check_vector
other = _check_vector(other)
ol = Vector(0)
for i, v in enumerate(self.args):
ol += v[0] * v[2] * (v[1] & other)
return ol

def __rsub__(self, other):
return (-1 * self) + other

def __rxor__(self, other):
"""For a cross product in the form: Vector x Dyadic

Parameters
==========

other : Vector
The Vector that we are crossing this Dyadic with

Examples
========

>>> from sympy.physics.vector import ReferenceFrame, outer, cross
>>> N = ReferenceFrame('N')
>>> d = outer(N.x, N.x)
>>> cross(N.y, d)
- (N.z|N.x)

"""

from sympy.physics.vector.vector import _check_vector
other = _check_vector(other)
ol = Dyadic(0)
for i, v in enumerate(self.args):
ol += v[0] * ((other ^ v[1]) | v[2])
return ol

def __str__(self, printer=None):
"""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 i, v in enumerate(ar):
# if the coef of the dyadic is 1, we skip the 1
if ar[i][0] == 1:
ol.append(' + (' + str(ar[i][1]) + '|' + str(ar[i][2]) + ')')
# if the coef of the dyadic is -1, we skip the 1
elif ar[i][0] == -1:
ol.append(' - (' + str(ar[i][1]) + '|' + str(ar[i][2]) + ')')
# If the coefficient of the dyadic is not 1 or -1,
# we might wrap it in parentheses, for readability.
elif ar[i][0] != 0:
arg_str = VectorStrPrinter().doprint(ar[i][0])
if isinstance(ar[i][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 + '*(' + str(ar[i][1]) +
'|' + str(ar[i][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 __xor__(self, other):
"""For a cross product in the form: Dyadic x Vector.

Parameters
==========

other : Vector
The Vector that we are crossing this Dyadic 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 i, v in enumerate(self.args):
ol += v[0] * (v[1] | (v[2] ^ other))
return ol

_sympystr = __str__
_sympyrepr = _sympystr
__repr__ = __str__
__radd__ = __add__
__rmul__ = __mul__

[docs]    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
>>> 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)

[docs]    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
>>> from sympy.physics.vector import ReferenceFrame, Vector
>>> Vector.simp = True
>>> 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))
>>> 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)

[docs]    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))

[docs]    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
>>> 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)

[docs]    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

[docs]    def subs(self, *args, **kwargs):
"""Substituion 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))

[docs]    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|c)
return out

dot = __and__
cross = __xor__

def _check_dyadic(other):
if not isinstance(other, Dyadic):
raise TypeError('A Dyadic must be supplied')
return other