123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- from typing import Type
- from sympy.vector.basisdependent import (BasisDependent, BasisDependentAdd,
- BasisDependentMul, BasisDependentZero)
- from sympy.core import S, Pow
- from sympy.core.expr import AtomicExpr
- from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
- import sympy.vector
- class Dyadic(BasisDependent):
- """
- Super class for all Dyadic-classes.
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Dyadic_tensor
- .. [2] Kane, T., Levinson, D. Dynamics Theory and Applications. 1985
- McGraw-Hill
- """
- _op_priority = 13.0
- _expr_type = None # type: Type[Dyadic]
- _mul_func = None # type: Type[Dyadic]
- _add_func = None # type: Type[Dyadic]
- _zero_func = None # type: Type[Dyadic]
- _base_func = None # type: Type[Dyadic]
- zero = None # type: DyadicZero
- @property
- def components(self):
- """
- Returns the components of this dyadic in the form of a
- Python dictionary mapping BaseDyadic instances to the
- corresponding measure numbers.
- """
- # The '_components' attribute is defined according to the
- # subclass of Dyadic the instance belongs to.
- return self._components
- def dot(self, other):
- """
- Returns the dot product(also called inner product) of this
- Dyadic, with another Dyadic or Vector.
- If 'other' is a Dyadic, this returns a Dyadic. Else, it returns
- a Vector (unless an error is encountered).
- Parameters
- ==========
- other : Dyadic/Vector
- The other Dyadic or Vector to take the inner product with
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> N = CoordSys3D('N')
- >>> D1 = N.i.outer(N.j)
- >>> D2 = N.j.outer(N.j)
- >>> D1.dot(D2)
- (N.i|N.j)
- >>> D1.dot(N.j)
- N.i
- """
- Vector = sympy.vector.Vector
- if isinstance(other, BasisDependentZero):
- return Vector.zero
- elif isinstance(other, Vector):
- outvec = Vector.zero
- for k, v in self.components.items():
- vect_dot = k.args[1].dot(other)
- outvec += vect_dot * v * k.args[0]
- return outvec
- elif isinstance(other, Dyadic):
- outdyad = Dyadic.zero
- for k1, v1 in self.components.items():
- for k2, v2 in other.components.items():
- vect_dot = k1.args[1].dot(k2.args[0])
- outer_product = k1.args[0].outer(k2.args[1])
- outdyad += vect_dot * v1 * v2 * outer_product
- return outdyad
- else:
- raise TypeError("Inner product is not defined for " +
- str(type(other)) + " and Dyadics.")
- def __and__(self, other):
- return self.dot(other)
- __and__.__doc__ = dot.__doc__
- def cross(self, other):
- """
- Returns the cross product between this Dyadic, and a Vector, as a
- Vector instance.
- Parameters
- ==========
- other : Vector
- The Vector that we are crossing this Dyadic with
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> N = CoordSys3D('N')
- >>> d = N.i.outer(N.i)
- >>> d.cross(N.j)
- (N.i|N.k)
- """
- Vector = sympy.vector.Vector
- if other == Vector.zero:
- return Dyadic.zero
- elif isinstance(other, Vector):
- outdyad = Dyadic.zero
- for k, v in self.components.items():
- cross_product = k.args[1].cross(other)
- outer = k.args[0].outer(cross_product)
- outdyad += v * outer
- return outdyad
- else:
- raise TypeError(str(type(other)) + " not supported for " +
- "cross with dyadics")
- def __xor__(self, other):
- return self.cross(other)
- __xor__.__doc__ = cross.__doc__
- def to_matrix(self, system, second_system=None):
- """
- Returns the matrix form of the dyadic with respect to one or two
- coordinate systems.
- Parameters
- ==========
- system : CoordSys3D
- The coordinate system that the rows and columns of the matrix
- correspond to. If a second system is provided, this
- only corresponds to the rows of the matrix.
- second_system : CoordSys3D, optional, default=None
- The coordinate system that the columns of the matrix correspond
- to.
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> N = CoordSys3D('N')
- >>> v = N.i + 2*N.j
- >>> d = v.outer(N.i)
- >>> d.to_matrix(N)
- Matrix([
- [1, 0, 0],
- [2, 0, 0],
- [0, 0, 0]])
- >>> from sympy import Symbol
- >>> q = Symbol('q')
- >>> P = N.orient_new_axis('P', q, N.k)
- >>> d.to_matrix(N, P)
- Matrix([
- [ cos(q), -sin(q), 0],
- [2*cos(q), -2*sin(q), 0],
- [ 0, 0, 0]])
- """
- if second_system is None:
- second_system = system
- return Matrix([i.dot(self).dot(j) for i in system for j in
- second_system]).reshape(3, 3)
- def _div_helper(one, other):
- """ Helper for division involving dyadics """
- if isinstance(one, Dyadic) and isinstance(other, Dyadic):
- raise TypeError("Cannot divide two dyadics")
- elif isinstance(one, Dyadic):
- return DyadicMul(one, Pow(other, S.NegativeOne))
- else:
- raise TypeError("Cannot divide by a dyadic")
- class BaseDyadic(Dyadic, AtomicExpr):
- """
- Class to denote a base dyadic tensor component.
- """
- def __new__(cls, vector1, vector2):
- Vector = sympy.vector.Vector
- BaseVector = sympy.vector.BaseVector
- VectorZero = sympy.vector.VectorZero
- # Verify arguments
- if not isinstance(vector1, (BaseVector, VectorZero)) or \
- not isinstance(vector2, (BaseVector, VectorZero)):
- raise TypeError("BaseDyadic cannot be composed of non-base " +
- "vectors")
- # Handle special case of zero vector
- elif vector1 == Vector.zero or vector2 == Vector.zero:
- return Dyadic.zero
- # Initialize instance
- obj = super().__new__(cls, vector1, vector2)
- obj._base_instance = obj
- obj._measure_number = 1
- obj._components = {obj: S.One}
- obj._sys = vector1._sys
- obj._pretty_form = ('(' + vector1._pretty_form + '|' +
- vector2._pretty_form + ')')
- obj._latex_form = (r'\left(' + vector1._latex_form + r"{\middle|}" +
- vector2._latex_form + r'\right)')
- return obj
- def _sympystr(self, printer):
- return "({}|{})".format(
- printer._print(self.args[0]), printer._print(self.args[1]))
- def _sympyrepr(self, printer):
- return "BaseDyadic({}, {})".format(
- printer._print(self.args[0]), printer._print(self.args[1]))
- class DyadicMul(BasisDependentMul, Dyadic):
- """ Products of scalars and BaseDyadics """
- def __new__(cls, *args, **options):
- obj = BasisDependentMul.__new__(cls, *args, **options)
- return obj
- @property
- def base_dyadic(self):
- """ The BaseDyadic involved in the product. """
- return self._base_instance
- @property
- def measure_number(self):
- """ The scalar expression involved in the definition of
- this DyadicMul.
- """
- return self._measure_number
- class DyadicAdd(BasisDependentAdd, Dyadic):
- """ Class to hold dyadic sums """
- def __new__(cls, *args, **options):
- obj = BasisDependentAdd.__new__(cls, *args, **options)
- return obj
- def _sympystr(self, printer):
- items = list(self.components.items())
- items.sort(key=lambda x: x[0].__str__())
- return " + ".join(printer._print(k * v) for k, v in items)
- class DyadicZero(BasisDependentZero, Dyadic):
- """
- Class to denote a zero dyadic
- """
- _op_priority = 13.1
- _pretty_form = '(0|0)'
- _latex_form = r'(\mathbf{\hat{0}}|\mathbf{\hat{0}})'
- def __new__(cls):
- obj = BasisDependentZero.__new__(cls)
- return obj
- Dyadic._expr_type = Dyadic
- Dyadic._mul_func = DyadicMul
- Dyadic._add_func = DyadicAdd
- Dyadic._zero_func = DyadicZero
- Dyadic._base_func = BaseDyadic
- Dyadic.zero = DyadicZero()
|