Spaces:
Sleeping
Sleeping
from __future__ import annotations | |
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: type[Dyadic] | |
_mul_func: type[Dyadic] | |
_add_func: type[Dyadic] | |
_zero_func: type[Dyadic] | |
_base_func: type[Dyadic] | |
zero: DyadicZero | |
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 | |
def base_dyadic(self): | |
""" The BaseDyadic involved in the product. """ | |
return self._base_instance | |
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() | |