dyadic.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. from typing import Type
  2. from sympy.vector.basisdependent import (BasisDependent, BasisDependentAdd,
  3. BasisDependentMul, BasisDependentZero)
  4. from sympy.core import S, Pow
  5. from sympy.core.expr import AtomicExpr
  6. from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
  7. import sympy.vector
  8. class Dyadic(BasisDependent):
  9. """
  10. Super class for all Dyadic-classes.
  11. References
  12. ==========
  13. .. [1] https://en.wikipedia.org/wiki/Dyadic_tensor
  14. .. [2] Kane, T., Levinson, D. Dynamics Theory and Applications. 1985
  15. McGraw-Hill
  16. """
  17. _op_priority = 13.0
  18. _expr_type = None # type: Type[Dyadic]
  19. _mul_func = None # type: Type[Dyadic]
  20. _add_func = None # type: Type[Dyadic]
  21. _zero_func = None # type: Type[Dyadic]
  22. _base_func = None # type: Type[Dyadic]
  23. zero = None # type: DyadicZero
  24. @property
  25. def components(self):
  26. """
  27. Returns the components of this dyadic in the form of a
  28. Python dictionary mapping BaseDyadic instances to the
  29. corresponding measure numbers.
  30. """
  31. # The '_components' attribute is defined according to the
  32. # subclass of Dyadic the instance belongs to.
  33. return self._components
  34. def dot(self, other):
  35. """
  36. Returns the dot product(also called inner product) of this
  37. Dyadic, with another Dyadic or Vector.
  38. If 'other' is a Dyadic, this returns a Dyadic. Else, it returns
  39. a Vector (unless an error is encountered).
  40. Parameters
  41. ==========
  42. other : Dyadic/Vector
  43. The other Dyadic or Vector to take the inner product with
  44. Examples
  45. ========
  46. >>> from sympy.vector import CoordSys3D
  47. >>> N = CoordSys3D('N')
  48. >>> D1 = N.i.outer(N.j)
  49. >>> D2 = N.j.outer(N.j)
  50. >>> D1.dot(D2)
  51. (N.i|N.j)
  52. >>> D1.dot(N.j)
  53. N.i
  54. """
  55. Vector = sympy.vector.Vector
  56. if isinstance(other, BasisDependentZero):
  57. return Vector.zero
  58. elif isinstance(other, Vector):
  59. outvec = Vector.zero
  60. for k, v in self.components.items():
  61. vect_dot = k.args[1].dot(other)
  62. outvec += vect_dot * v * k.args[0]
  63. return outvec
  64. elif isinstance(other, Dyadic):
  65. outdyad = Dyadic.zero
  66. for k1, v1 in self.components.items():
  67. for k2, v2 in other.components.items():
  68. vect_dot = k1.args[1].dot(k2.args[0])
  69. outer_product = k1.args[0].outer(k2.args[1])
  70. outdyad += vect_dot * v1 * v2 * outer_product
  71. return outdyad
  72. else:
  73. raise TypeError("Inner product is not defined for " +
  74. str(type(other)) + " and Dyadics.")
  75. def __and__(self, other):
  76. return self.dot(other)
  77. __and__.__doc__ = dot.__doc__
  78. def cross(self, other):
  79. """
  80. Returns the cross product between this Dyadic, and a Vector, as a
  81. Vector instance.
  82. Parameters
  83. ==========
  84. other : Vector
  85. The Vector that we are crossing this Dyadic with
  86. Examples
  87. ========
  88. >>> from sympy.vector import CoordSys3D
  89. >>> N = CoordSys3D('N')
  90. >>> d = N.i.outer(N.i)
  91. >>> d.cross(N.j)
  92. (N.i|N.k)
  93. """
  94. Vector = sympy.vector.Vector
  95. if other == Vector.zero:
  96. return Dyadic.zero
  97. elif isinstance(other, Vector):
  98. outdyad = Dyadic.zero
  99. for k, v in self.components.items():
  100. cross_product = k.args[1].cross(other)
  101. outer = k.args[0].outer(cross_product)
  102. outdyad += v * outer
  103. return outdyad
  104. else:
  105. raise TypeError(str(type(other)) + " not supported for " +
  106. "cross with dyadics")
  107. def __xor__(self, other):
  108. return self.cross(other)
  109. __xor__.__doc__ = cross.__doc__
  110. def to_matrix(self, system, second_system=None):
  111. """
  112. Returns the matrix form of the dyadic with respect to one or two
  113. coordinate systems.
  114. Parameters
  115. ==========
  116. system : CoordSys3D
  117. The coordinate system that the rows and columns of the matrix
  118. correspond to. If a second system is provided, this
  119. only corresponds to the rows of the matrix.
  120. second_system : CoordSys3D, optional, default=None
  121. The coordinate system that the columns of the matrix correspond
  122. to.
  123. Examples
  124. ========
  125. >>> from sympy.vector import CoordSys3D
  126. >>> N = CoordSys3D('N')
  127. >>> v = N.i + 2*N.j
  128. >>> d = v.outer(N.i)
  129. >>> d.to_matrix(N)
  130. Matrix([
  131. [1, 0, 0],
  132. [2, 0, 0],
  133. [0, 0, 0]])
  134. >>> from sympy import Symbol
  135. >>> q = Symbol('q')
  136. >>> P = N.orient_new_axis('P', q, N.k)
  137. >>> d.to_matrix(N, P)
  138. Matrix([
  139. [ cos(q), -sin(q), 0],
  140. [2*cos(q), -2*sin(q), 0],
  141. [ 0, 0, 0]])
  142. """
  143. if second_system is None:
  144. second_system = system
  145. return Matrix([i.dot(self).dot(j) for i in system for j in
  146. second_system]).reshape(3, 3)
  147. def _div_helper(one, other):
  148. """ Helper for division involving dyadics """
  149. if isinstance(one, Dyadic) and isinstance(other, Dyadic):
  150. raise TypeError("Cannot divide two dyadics")
  151. elif isinstance(one, Dyadic):
  152. return DyadicMul(one, Pow(other, S.NegativeOne))
  153. else:
  154. raise TypeError("Cannot divide by a dyadic")
  155. class BaseDyadic(Dyadic, AtomicExpr):
  156. """
  157. Class to denote a base dyadic tensor component.
  158. """
  159. def __new__(cls, vector1, vector2):
  160. Vector = sympy.vector.Vector
  161. BaseVector = sympy.vector.BaseVector
  162. VectorZero = sympy.vector.VectorZero
  163. # Verify arguments
  164. if not isinstance(vector1, (BaseVector, VectorZero)) or \
  165. not isinstance(vector2, (BaseVector, VectorZero)):
  166. raise TypeError("BaseDyadic cannot be composed of non-base " +
  167. "vectors")
  168. # Handle special case of zero vector
  169. elif vector1 == Vector.zero or vector2 == Vector.zero:
  170. return Dyadic.zero
  171. # Initialize instance
  172. obj = super().__new__(cls, vector1, vector2)
  173. obj._base_instance = obj
  174. obj._measure_number = 1
  175. obj._components = {obj: S.One}
  176. obj._sys = vector1._sys
  177. obj._pretty_form = ('(' + vector1._pretty_form + '|' +
  178. vector2._pretty_form + ')')
  179. obj._latex_form = (r'\left(' + vector1._latex_form + r"{\middle|}" +
  180. vector2._latex_form + r'\right)')
  181. return obj
  182. def _sympystr(self, printer):
  183. return "({}|{})".format(
  184. printer._print(self.args[0]), printer._print(self.args[1]))
  185. def _sympyrepr(self, printer):
  186. return "BaseDyadic({}, {})".format(
  187. printer._print(self.args[0]), printer._print(self.args[1]))
  188. class DyadicMul(BasisDependentMul, Dyadic):
  189. """ Products of scalars and BaseDyadics """
  190. def __new__(cls, *args, **options):
  191. obj = BasisDependentMul.__new__(cls, *args, **options)
  192. return obj
  193. @property
  194. def base_dyadic(self):
  195. """ The BaseDyadic involved in the product. """
  196. return self._base_instance
  197. @property
  198. def measure_number(self):
  199. """ The scalar expression involved in the definition of
  200. this DyadicMul.
  201. """
  202. return self._measure_number
  203. class DyadicAdd(BasisDependentAdd, Dyadic):
  204. """ Class to hold dyadic sums """
  205. def __new__(cls, *args, **options):
  206. obj = BasisDependentAdd.__new__(cls, *args, **options)
  207. return obj
  208. def _sympystr(self, printer):
  209. items = list(self.components.items())
  210. items.sort(key=lambda x: x[0].__str__())
  211. return " + ".join(printer._print(k * v) for k, v in items)
  212. class DyadicZero(BasisDependentZero, Dyadic):
  213. """
  214. Class to denote a zero dyadic
  215. """
  216. _op_priority = 13.1
  217. _pretty_form = '(0|0)'
  218. _latex_form = r'(\mathbf{\hat{0}}|\mathbf{\hat{0}})'
  219. def __new__(cls):
  220. obj = BasisDependentZero.__new__(cls)
  221. return obj
  222. Dyadic._expr_type = Dyadic
  223. Dyadic._mul_func = DyadicMul
  224. Dyadic._add_func = DyadicAdd
  225. Dyadic._zero_func = DyadicZero
  226. Dyadic._base_func = BaseDyadic
  227. Dyadic.zero = DyadicZero()