|
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 |
|
|
|
@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. |
|
|
|
""" |
|
|
|
|
|
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 |
|
|
|
if not isinstance(vector1, (BaseVector, VectorZero)) or \ |
|
not isinstance(vector2, (BaseVector, VectorZero)): |
|
raise TypeError("BaseDyadic cannot be composed of non-base " + |
|
"vectors") |
|
|
|
elif vector1 == Vector.zero or vector2 == Vector.zero: |
|
return Dyadic.zero |
|
|
|
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() |
|
|