123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- from sympy.core.backend 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)
- 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 __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 __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, v in enumerate(newlist):
- newlist[i] = (other * newlist[i][0], newlist[i][1],
- newlist[i][2])
- return Dyadic(newlist)
- 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 i, v in enumerate(ar):
- # if the coef of the dyadic is 1, we skip the 1
- if ar[i][0] == 1:
- ol.append(' + ' + printer._print(ar[i][1]) + r"\otimes " +
- printer._print(ar[i][2]))
- # if the coef of the dyadic is -1, we skip the 1
- elif ar[i][0] == -1:
- ol.append(' - ' +
- printer._print(ar[i][1]) +
- r"\otimes " +
- printer._print(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 = printer._print(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 + printer._print(ar[i][1]) +
- r"\otimes " + printer._print(ar[i][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 i, v in enumerate(ar):
- # if the coef of the dyadic is 1, we skip the 1
- if ar[i][0] == 1:
- ol.extend([" + ",
- 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([" - ",
- 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("-"):
- arg_str = arg_str[1:]
- str_start = " - "
- else:
- str_start = " + "
- ol.extend([str_start, arg_str, " ",
- mpp.doprint(ar[i][1]),
- bar,
- mpp.doprint(ar[i][2])])
- outstr = "".join(ol)
- if outstr.startswith(" + "):
- 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 _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 i, v in enumerate(ar):
- # if the coef of the dyadic is 1, we skip the 1
- if ar[i][0] == 1:
- ol.append(' + (' + printer._print(ar[i][1]) + '|' +
- printer._print(ar[i][2]) + ')')
- # if the coef of the dyadic is -1, we skip the 1
- elif ar[i][0] == -1:
- ol.append(' - (' + printer._print(ar[i][1]) + '|' +
- printer._print(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 = printer._print(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 + '*(' +
- printer._print(ar[i][1]) +
- '|' + printer._print(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
- __radd__ = __add__
- __rmul__ = __mul__
- 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
- >>> 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)
- 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 | c)
- return out
- dot = __and__
- cross = __xor__
- 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
|