switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.vector import (Vector, VectorAdd, VectorMul,
|
||||
BaseVector, VectorZero, Cross, Dot, cross, dot)
|
||||
from sympy.vector.dyadic import (Dyadic, DyadicAdd, DyadicMul,
|
||||
BaseDyadic, DyadicZero)
|
||||
from sympy.vector.scalar import BaseScalar
|
||||
from sympy.vector.deloperator import Del
|
||||
from sympy.vector.functions import (express, matrix_to_vector,
|
||||
laplacian, is_conservative,
|
||||
is_solenoidal, scalar_potential,
|
||||
directional_derivative,
|
||||
scalar_potential_difference)
|
||||
from sympy.vector.point import Point
|
||||
from sympy.vector.orienters import (AxisOrienter, BodyOrienter,
|
||||
SpaceOrienter, QuaternionOrienter)
|
||||
from sympy.vector.operators import Gradient, Divergence, Curl, Laplacian, gradient, curl, divergence
|
||||
from sympy.vector.implicitregion import ImplicitRegion
|
||||
from sympy.vector.parametricregion import (ParametricRegion, parametric_region_list)
|
||||
from sympy.vector.integrals import (ParametricIntegral, vector_integrate)
|
||||
from sympy.vector.kind import VectorKind
|
||||
|
||||
__all__ = [
|
||||
'Vector', 'VectorAdd', 'VectorMul', 'BaseVector', 'VectorZero', 'Cross',
|
||||
'Dot', 'cross', 'dot',
|
||||
|
||||
'VectorKind',
|
||||
|
||||
'Dyadic', 'DyadicAdd', 'DyadicMul', 'BaseDyadic', 'DyadicZero',
|
||||
|
||||
'BaseScalar',
|
||||
|
||||
'Del',
|
||||
|
||||
'CoordSys3D',
|
||||
|
||||
'express', 'matrix_to_vector', 'laplacian', 'is_conservative',
|
||||
'is_solenoidal', 'scalar_potential', 'directional_derivative',
|
||||
'scalar_potential_difference',
|
||||
|
||||
'Point',
|
||||
|
||||
'AxisOrienter', 'BodyOrienter', 'SpaceOrienter', 'QuaternionOrienter',
|
||||
|
||||
'Gradient', 'Divergence', 'Curl', 'Laplacian', 'gradient', 'curl',
|
||||
'divergence',
|
||||
|
||||
'ParametricRegion', 'parametric_region_list', 'ImplicitRegion',
|
||||
|
||||
'ParametricIntegral', 'vector_integrate',
|
||||
]
|
||||
@@ -0,0 +1,374 @@
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sympy.simplify import simplify as simp, trigsimp as tsimp # type: ignore
|
||||
from sympy.core.decorators import call_highest_priority, _sympifyit
|
||||
from sympy.core.assumptions import StdFactKB
|
||||
from sympy.core.function import diff as df
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.polys.polytools import factor as fctr
|
||||
from sympy.core import S, Add, Mul
|
||||
from sympy.core.expr import Expr
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sympy.vector.vector import BaseVector
|
||||
|
||||
|
||||
class BasisDependent(Expr):
|
||||
"""
|
||||
Super class containing functionality common to vectors and
|
||||
dyadics.
|
||||
Named so because the representation of these quantities in
|
||||
sympy.vector is dependent on the basis they are expressed in.
|
||||
"""
|
||||
|
||||
zero: BasisDependentZero
|
||||
|
||||
@call_highest_priority('__radd__')
|
||||
def __add__(self, other):
|
||||
return self._add_func(self, other)
|
||||
|
||||
@call_highest_priority('__add__')
|
||||
def __radd__(self, other):
|
||||
return self._add_func(other, self)
|
||||
|
||||
@call_highest_priority('__rsub__')
|
||||
def __sub__(self, other):
|
||||
return self._add_func(self, -other)
|
||||
|
||||
@call_highest_priority('__sub__')
|
||||
def __rsub__(self, other):
|
||||
return self._add_func(other, -self)
|
||||
|
||||
@_sympifyit('other', NotImplemented)
|
||||
@call_highest_priority('__rmul__')
|
||||
def __mul__(self, other):
|
||||
return self._mul_func(self, other)
|
||||
|
||||
@_sympifyit('other', NotImplemented)
|
||||
@call_highest_priority('__mul__')
|
||||
def __rmul__(self, other):
|
||||
return self._mul_func(other, self)
|
||||
|
||||
def __neg__(self):
|
||||
return self._mul_func(S.NegativeOne, self)
|
||||
|
||||
@_sympifyit('other', NotImplemented)
|
||||
@call_highest_priority('__rtruediv__')
|
||||
def __truediv__(self, other):
|
||||
return self._div_helper(other)
|
||||
|
||||
@call_highest_priority('__truediv__')
|
||||
def __rtruediv__(self, other):
|
||||
return TypeError("Invalid divisor for division")
|
||||
|
||||
def evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False):
|
||||
"""
|
||||
Implements the SymPy evalf routine for this quantity.
|
||||
|
||||
evalf's documentation
|
||||
=====================
|
||||
|
||||
"""
|
||||
options = {'subs':subs, 'maxn':maxn, 'chop':chop, 'strict':strict,
|
||||
'quad':quad, 'verbose':verbose}
|
||||
vec = self.zero
|
||||
for k, v in self.components.items():
|
||||
vec += v.evalf(n, **options) * k
|
||||
return vec
|
||||
|
||||
evalf.__doc__ += Expr.evalf.__doc__ # type: ignore
|
||||
|
||||
n = evalf # type: ignore
|
||||
|
||||
def simplify(self, **kwargs):
|
||||
"""
|
||||
Implements the SymPy simplify routine for this quantity.
|
||||
|
||||
simplify's documentation
|
||||
========================
|
||||
|
||||
"""
|
||||
simp_components = [simp(v, **kwargs) * k for
|
||||
k, v in self.components.items()]
|
||||
return self._add_func(*simp_components)
|
||||
|
||||
simplify.__doc__ += simp.__doc__ # type: ignore
|
||||
|
||||
def trigsimp(self, **opts):
|
||||
"""
|
||||
Implements the SymPy trigsimp routine, for this quantity.
|
||||
|
||||
trigsimp's documentation
|
||||
========================
|
||||
|
||||
"""
|
||||
trig_components = [tsimp(v, **opts) * k for
|
||||
k, v in self.components.items()]
|
||||
return self._add_func(*trig_components)
|
||||
|
||||
trigsimp.__doc__ += tsimp.__doc__ # type: ignore
|
||||
|
||||
def _eval_simplify(self, **kwargs):
|
||||
return self.simplify(**kwargs)
|
||||
|
||||
def _eval_trigsimp(self, **opts):
|
||||
return self.trigsimp(**opts)
|
||||
|
||||
def _eval_derivative(self, wrt):
|
||||
return self.diff(wrt)
|
||||
|
||||
def _eval_Integral(self, *symbols, **assumptions):
|
||||
integral_components = [Integral(v, *symbols, **assumptions) * k
|
||||
for k, v in self.components.items()]
|
||||
return self._add_func(*integral_components)
|
||||
|
||||
def as_numer_denom(self):
|
||||
"""
|
||||
Returns the expression as a tuple wrt the following
|
||||
transformation -
|
||||
|
||||
expression -> a/b -> a, b
|
||||
|
||||
"""
|
||||
return self, S.One
|
||||
|
||||
def factor(self, *args, **kwargs):
|
||||
"""
|
||||
Implements the SymPy factor routine, on the scalar parts
|
||||
of a basis-dependent expression.
|
||||
|
||||
factor's documentation
|
||||
========================
|
||||
|
||||
"""
|
||||
fctr_components = [fctr(v, *args, **kwargs) * k for
|
||||
k, v in self.components.items()]
|
||||
return self._add_func(*fctr_components)
|
||||
|
||||
factor.__doc__ += fctr.__doc__ # type: ignore
|
||||
|
||||
def as_coeff_Mul(self, rational=False):
|
||||
"""Efficiently extract the coefficient of a product."""
|
||||
return (S.One, self)
|
||||
|
||||
def as_coeff_add(self, *deps):
|
||||
"""Efficiently extract the coefficient of a summation."""
|
||||
return 0, tuple(x * self.components[x] for x in self.components)
|
||||
|
||||
def diff(self, *args, **kwargs):
|
||||
"""
|
||||
Implements the SymPy diff routine, for vectors.
|
||||
|
||||
diff's documentation
|
||||
========================
|
||||
|
||||
"""
|
||||
for x in args:
|
||||
if isinstance(x, BasisDependent):
|
||||
raise TypeError("Invalid arg for differentiation")
|
||||
diff_components = [df(v, *args, **kwargs) * k for
|
||||
k, v in self.components.items()]
|
||||
return self._add_func(*diff_components)
|
||||
|
||||
diff.__doc__ += df.__doc__ # type: ignore
|
||||
|
||||
def doit(self, **hints):
|
||||
"""Calls .doit() on each term in the Dyadic"""
|
||||
doit_components = [self.components[x].doit(**hints) * x
|
||||
for x in self.components]
|
||||
return self._add_func(*doit_components)
|
||||
|
||||
|
||||
class BasisDependentAdd(BasisDependent, Add):
|
||||
"""
|
||||
Denotes sum of basis dependent quantities such that they cannot
|
||||
be expressed as base or Mul instances.
|
||||
"""
|
||||
|
||||
def __new__(cls, *args, **options):
|
||||
components = {}
|
||||
|
||||
# Check each arg and simultaneously learn the components
|
||||
for arg in args:
|
||||
if not isinstance(arg, cls._expr_type):
|
||||
if isinstance(arg, Mul):
|
||||
arg = cls._mul_func(*(arg.args))
|
||||
elif isinstance(arg, Add):
|
||||
arg = cls._add_func(*(arg.args))
|
||||
else:
|
||||
raise TypeError(str(arg) +
|
||||
" cannot be interpreted correctly")
|
||||
# If argument is zero, ignore
|
||||
if arg == cls.zero:
|
||||
continue
|
||||
# Else, update components accordingly
|
||||
for x in arg.components:
|
||||
components[x] = components.get(x, 0) + arg.components[x]
|
||||
|
||||
temp = list(components.keys())
|
||||
for x in temp:
|
||||
if components[x] == 0:
|
||||
del components[x]
|
||||
|
||||
# Handle case of zero vector
|
||||
if len(components) == 0:
|
||||
return cls.zero
|
||||
|
||||
# Build object
|
||||
newargs = [x * components[x] for x in components]
|
||||
obj = super().__new__(cls, *newargs, **options)
|
||||
if isinstance(obj, Mul):
|
||||
return cls._mul_func(*obj.args)
|
||||
assumptions = {'commutative': True}
|
||||
obj._assumptions = StdFactKB(assumptions)
|
||||
obj._components = components
|
||||
obj._sys = (list(components.keys()))[0]._sys
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class BasisDependentMul(BasisDependent, Mul):
|
||||
"""
|
||||
Denotes product of base- basis dependent quantity with a scalar.
|
||||
"""
|
||||
|
||||
def __new__(cls, *args, **options):
|
||||
obj = cls._new(*args, **options)
|
||||
return obj
|
||||
|
||||
def _new_rawargs(self, *args):
|
||||
# XXX: This is needed because Add.flatten() uses it but the default
|
||||
# implementation does not work for Vectors because they assign
|
||||
# attributes outside of .args.
|
||||
return type(self)(*args)
|
||||
|
||||
@classmethod
|
||||
def _new(cls, *args, **options):
|
||||
from sympy.vector import Cross, Dot, Curl, Gradient
|
||||
count = 0
|
||||
measure_number = S.One
|
||||
zeroflag = False
|
||||
extra_args = []
|
||||
|
||||
# Determine the component and check arguments
|
||||
# Also keep a count to ensure two vectors aren't
|
||||
# being multiplied
|
||||
for arg in args:
|
||||
if isinstance(arg, cls._zero_func):
|
||||
count += 1
|
||||
zeroflag = True
|
||||
elif arg == S.Zero:
|
||||
zeroflag = True
|
||||
elif isinstance(arg, (cls._base_func, cls._mul_func)):
|
||||
count += 1
|
||||
expr = arg._base_instance
|
||||
measure_number *= arg._measure_number
|
||||
elif isinstance(arg, cls._add_func):
|
||||
count += 1
|
||||
expr = arg
|
||||
elif isinstance(arg, (Cross, Dot, Curl, Gradient)):
|
||||
extra_args.append(arg)
|
||||
else:
|
||||
measure_number *= arg
|
||||
# Make sure incompatible types weren't multiplied
|
||||
if count > 1:
|
||||
raise ValueError("Invalid multiplication")
|
||||
elif count == 0:
|
||||
return Mul(*args, **options)
|
||||
# Handle zero vector case
|
||||
if zeroflag:
|
||||
return cls.zero
|
||||
|
||||
# If one of the args was a VectorAdd, return an
|
||||
# appropriate VectorAdd instance
|
||||
if isinstance(expr, cls._add_func):
|
||||
newargs = [cls._mul_func(measure_number, x) for
|
||||
x in expr.args]
|
||||
return cls._add_func(*newargs)
|
||||
|
||||
obj = super().__new__(cls, measure_number,
|
||||
expr._base_instance,
|
||||
*extra_args,
|
||||
**options)
|
||||
if isinstance(obj, Add):
|
||||
return cls._add_func(*obj.args)
|
||||
obj._base_instance = expr._base_instance
|
||||
obj._measure_number = measure_number
|
||||
assumptions = {'commutative': True}
|
||||
obj._assumptions = StdFactKB(assumptions)
|
||||
obj._components = {expr._base_instance: measure_number}
|
||||
obj._sys = expr._base_instance._sys
|
||||
|
||||
return obj
|
||||
|
||||
def _sympystr(self, printer):
|
||||
measure_str = printer._print(self._measure_number)
|
||||
if ('(' in measure_str or '-' in measure_str or
|
||||
'+' in measure_str):
|
||||
measure_str = '(' + measure_str + ')'
|
||||
return measure_str + '*' + printer._print(self._base_instance)
|
||||
|
||||
|
||||
class BasisDependentZero(BasisDependent):
|
||||
"""
|
||||
Class to denote a zero basis dependent instance.
|
||||
"""
|
||||
components: dict['BaseVector', Expr] = {}
|
||||
_latex_form: str
|
||||
|
||||
def __new__(cls):
|
||||
obj = super().__new__(cls)
|
||||
# Pre-compute a specific hash value for the zero vector
|
||||
# Use the same one always
|
||||
obj._hash = (S.Zero, cls).__hash__()
|
||||
return obj
|
||||
|
||||
def __hash__(self):
|
||||
return self._hash
|
||||
|
||||
@call_highest_priority('__req__')
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, self._zero_func)
|
||||
|
||||
__req__ = __eq__
|
||||
|
||||
@call_highest_priority('__radd__')
|
||||
def __add__(self, other):
|
||||
if isinstance(other, self._expr_type):
|
||||
return other
|
||||
else:
|
||||
raise TypeError("Invalid argument types for addition")
|
||||
|
||||
@call_highest_priority('__add__')
|
||||
def __radd__(self, other):
|
||||
if isinstance(other, self._expr_type):
|
||||
return other
|
||||
else:
|
||||
raise TypeError("Invalid argument types for addition")
|
||||
|
||||
@call_highest_priority('__rsub__')
|
||||
def __sub__(self, other):
|
||||
if isinstance(other, self._expr_type):
|
||||
return -other
|
||||
else:
|
||||
raise TypeError("Invalid argument types for subtraction")
|
||||
|
||||
@call_highest_priority('__sub__')
|
||||
def __rsub__(self, other):
|
||||
if isinstance(other, self._expr_type):
|
||||
return other
|
||||
else:
|
||||
raise TypeError("Invalid argument types for subtraction")
|
||||
|
||||
def __neg__(self):
|
||||
return self
|
||||
|
||||
def normalize(self):
|
||||
"""
|
||||
Returns the normalized version of this vector.
|
||||
"""
|
||||
return self
|
||||
|
||||
def _sympystr(self, printer):
|
||||
return '0'
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,121 @@
|
||||
from sympy.core import Basic
|
||||
from sympy.vector.operators import gradient, divergence, curl
|
||||
|
||||
|
||||
class Del(Basic):
|
||||
"""
|
||||
Represents the vector differential operator, usually represented in
|
||||
mathematical expressions as the 'nabla' symbol.
|
||||
"""
|
||||
|
||||
def __new__(cls):
|
||||
obj = super().__new__(cls)
|
||||
obj._name = "delop"
|
||||
return obj
|
||||
|
||||
def gradient(self, scalar_field, doit=False):
|
||||
"""
|
||||
Returns the gradient of the given scalar field, as a
|
||||
Vector instance.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
scalar_field : SymPy expression
|
||||
The scalar field to calculate the gradient of.
|
||||
|
||||
doit : bool
|
||||
If True, the result is returned after calling .doit() on
|
||||
each component. Else, the returned expression contains
|
||||
Derivative instances
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Del
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> delop = Del()
|
||||
>>> delop.gradient(9)
|
||||
0
|
||||
>>> delop(C.x*C.y*C.z).doit()
|
||||
C.y*C.z*C.i + C.x*C.z*C.j + C.x*C.y*C.k
|
||||
|
||||
"""
|
||||
|
||||
return gradient(scalar_field, doit=doit)
|
||||
|
||||
__call__ = gradient
|
||||
__call__.__doc__ = gradient.__doc__
|
||||
|
||||
def dot(self, vect, doit=False):
|
||||
"""
|
||||
Represents the dot product between this operator and a given
|
||||
vector - equal to the divergence of the vector field.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
vect : Vector
|
||||
The vector whose divergence is to be calculated.
|
||||
|
||||
doit : bool
|
||||
If True, the result is returned after calling .doit() on
|
||||
each component. Else, the returned expression contains
|
||||
Derivative instances
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Del
|
||||
>>> delop = Del()
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> delop.dot(C.x*C.i)
|
||||
Derivative(C.x, C.x)
|
||||
>>> v = C.x*C.y*C.z * (C.i + C.j + C.k)
|
||||
>>> (delop & v).doit()
|
||||
C.x*C.y + C.x*C.z + C.y*C.z
|
||||
|
||||
"""
|
||||
return divergence(vect, doit=doit)
|
||||
|
||||
__and__ = dot
|
||||
__and__.__doc__ = dot.__doc__
|
||||
|
||||
def cross(self, vect, doit=False):
|
||||
"""
|
||||
Represents the cross product between this operator and a given
|
||||
vector - equal to the curl of the vector field.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
vect : Vector
|
||||
The vector whose curl is to be calculated.
|
||||
|
||||
doit : bool
|
||||
If True, the result is returned after calling .doit() on
|
||||
each component. Else, the returned expression contains
|
||||
Derivative instances
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Del
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> delop = Del()
|
||||
>>> v = C.x*C.y*C.z * (C.i + C.j + C.k)
|
||||
>>> delop.cross(v, doit = True)
|
||||
(-C.x*C.y + C.x*C.z)*C.i + (C.x*C.y - C.y*C.z)*C.j +
|
||||
(-C.x*C.z + C.y*C.z)*C.k
|
||||
>>> (delop ^ C.i).doit()
|
||||
0
|
||||
|
||||
"""
|
||||
|
||||
return curl(vect, doit=doit)
|
||||
|
||||
__xor__ = cross
|
||||
__xor__.__doc__ = cross.__doc__
|
||||
|
||||
def _sympystr(self, printer):
|
||||
return self._name
|
||||
@@ -0,0 +1,285 @@
|
||||
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.
|
||||
|
||||
"""
|
||||
# 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()
|
||||
@@ -0,0 +1,513 @@
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.deloperator import Del
|
||||
from sympy.vector.scalar import BaseScalar
|
||||
from sympy.vector.vector import Vector, BaseVector
|
||||
from sympy.vector.operators import gradient, curl, divergence
|
||||
from sympy.core.function import diff
|
||||
from sympy.core.singleton import S
|
||||
from sympy.integrals.integrals import integrate
|
||||
from sympy.core import sympify
|
||||
from sympy.vector.dyadic import Dyadic
|
||||
|
||||
|
||||
def express(expr, system, system2=None, variables=False):
|
||||
"""
|
||||
Global function for 'express' functionality.
|
||||
|
||||
Re-expresses a Vector, Dyadic or scalar(sympyfiable) in the given
|
||||
coordinate system.
|
||||
|
||||
If 'variables' is True, then the coordinate variables (base scalars)
|
||||
of other coordinate systems present in the vector/scalar field or
|
||||
dyadic are also substituted in terms of the base scalars of the
|
||||
given system.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : Vector/Dyadic/scalar(sympyfiable)
|
||||
The expression to re-express in CoordSys3D 'system'
|
||||
|
||||
system: CoordSys3D
|
||||
The coordinate system the expr is to be expressed in
|
||||
|
||||
system2: CoordSys3D
|
||||
The other coordinate system required for re-expression
|
||||
(only for a Dyadic Expr)
|
||||
|
||||
variables : boolean
|
||||
Specifies whether to substitute the coordinate variables present
|
||||
in expr, in terms of those of parameter system
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy import Symbol, cos, sin
|
||||
>>> N = CoordSys3D('N')
|
||||
>>> q = Symbol('q')
|
||||
>>> B = N.orient_new_axis('B', q, N.k)
|
||||
>>> from sympy.vector import express
|
||||
>>> express(B.i, N)
|
||||
(cos(q))*N.i + (sin(q))*N.j
|
||||
>>> express(N.x, B, variables=True)
|
||||
B.x*cos(q) - B.y*sin(q)
|
||||
>>> d = N.i.outer(N.i)
|
||||
>>> express(d, B, N) == (cos(q))*(B.i|N.i) + (-sin(q))*(B.j|N.i)
|
||||
True
|
||||
|
||||
"""
|
||||
|
||||
if expr in (0, Vector.zero):
|
||||
return expr
|
||||
|
||||
if not isinstance(system, CoordSys3D):
|
||||
raise TypeError("system should be a CoordSys3D \
|
||||
instance")
|
||||
|
||||
if isinstance(expr, Vector):
|
||||
if system2 is not None:
|
||||
raise ValueError("system2 should not be provided for \
|
||||
Vectors")
|
||||
# Given expr is a Vector
|
||||
if variables:
|
||||
# If variables attribute is True, substitute
|
||||
# the coordinate variables in the Vector
|
||||
system_list = {x.system for x in expr.atoms(BaseScalar, BaseVector)} - {system}
|
||||
subs_dict = {}
|
||||
for f in system_list:
|
||||
subs_dict.update(f.scalar_map(system))
|
||||
expr = expr.subs(subs_dict)
|
||||
# Re-express in this coordinate system
|
||||
outvec = Vector.zero
|
||||
parts = expr.separate()
|
||||
for x in parts:
|
||||
if x != system:
|
||||
temp = system.rotation_matrix(x) * parts[x].to_matrix(x)
|
||||
outvec += matrix_to_vector(temp, system)
|
||||
else:
|
||||
outvec += parts[x]
|
||||
return outvec
|
||||
|
||||
elif isinstance(expr, Dyadic):
|
||||
if system2 is None:
|
||||
system2 = system
|
||||
if not isinstance(system2, CoordSys3D):
|
||||
raise TypeError("system2 should be a CoordSys3D \
|
||||
instance")
|
||||
outdyad = Dyadic.zero
|
||||
var = variables
|
||||
for k, v in expr.components.items():
|
||||
outdyad += (express(v, system, variables=var) *
|
||||
(express(k.args[0], system, variables=var) |
|
||||
express(k.args[1], system2, variables=var)))
|
||||
|
||||
return outdyad
|
||||
|
||||
else:
|
||||
if system2 is not None:
|
||||
raise ValueError("system2 should not be provided for \
|
||||
Vectors")
|
||||
if variables:
|
||||
# Given expr is a scalar field
|
||||
system_set = set()
|
||||
expr = sympify(expr)
|
||||
# Substitute all the coordinate variables
|
||||
for x in expr.atoms(BaseScalar):
|
||||
if x.system != system:
|
||||
system_set.add(x.system)
|
||||
subs_dict = {}
|
||||
for f in system_set:
|
||||
subs_dict.update(f.scalar_map(system))
|
||||
return expr.subs(subs_dict)
|
||||
return expr
|
||||
|
||||
|
||||
def directional_derivative(field, direction_vector):
|
||||
"""
|
||||
Returns the directional derivative of a scalar or vector field computed
|
||||
along a given vector in coordinate system which parameters are expressed.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
field : Vector or Scalar
|
||||
The scalar or vector field to compute the directional derivative of
|
||||
|
||||
direction_vector : Vector
|
||||
The vector to calculated directional derivative along them.
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, directional_derivative
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> f1 = R.x*R.y*R.z
|
||||
>>> v1 = 3*R.i + 4*R.j + R.k
|
||||
>>> directional_derivative(f1, v1)
|
||||
R.x*R.y + 4*R.x*R.z + 3*R.y*R.z
|
||||
>>> f2 = 5*R.x**2*R.z
|
||||
>>> directional_derivative(f2, v1)
|
||||
5*R.x**2 + 30*R.x*R.z
|
||||
|
||||
"""
|
||||
from sympy.vector.operators import _get_coord_systems
|
||||
coord_sys = _get_coord_systems(field)
|
||||
if len(coord_sys) > 0:
|
||||
# TODO: This gets a random coordinate system in case of multiple ones:
|
||||
coord_sys = next(iter(coord_sys))
|
||||
field = express(field, coord_sys, variables=True)
|
||||
i, j, k = coord_sys.base_vectors()
|
||||
x, y, z = coord_sys.base_scalars()
|
||||
out = Vector.dot(direction_vector, i) * diff(field, x)
|
||||
out += Vector.dot(direction_vector, j) * diff(field, y)
|
||||
out += Vector.dot(direction_vector, k) * diff(field, z)
|
||||
if out == 0 and isinstance(field, Vector):
|
||||
out = Vector.zero
|
||||
return out
|
||||
elif isinstance(field, Vector):
|
||||
return Vector.zero
|
||||
else:
|
||||
return S.Zero
|
||||
|
||||
|
||||
def laplacian(expr):
|
||||
"""
|
||||
Return the laplacian of the given field computed in terms of
|
||||
the base scalars of the given coordinate system.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : SymPy Expr or Vector
|
||||
expr denotes a scalar or vector field.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, laplacian
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> f = R.x**2*R.y**5*R.z
|
||||
>>> laplacian(f)
|
||||
20*R.x**2*R.y**3*R.z + 2*R.y**5*R.z
|
||||
>>> f = R.x**2*R.i + R.y**3*R.j + R.z**4*R.k
|
||||
>>> laplacian(f)
|
||||
2*R.i + 6*R.y*R.j + 12*R.z**2*R.k
|
||||
|
||||
"""
|
||||
|
||||
delop = Del()
|
||||
if expr.is_Vector:
|
||||
return (gradient(divergence(expr)) - curl(curl(expr))).doit()
|
||||
return delop.dot(delop(expr)).doit()
|
||||
|
||||
|
||||
def is_conservative(field):
|
||||
"""
|
||||
Checks if a field is conservative.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
field : Vector
|
||||
The field to check for conservative property
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy.vector import is_conservative
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> is_conservative(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k)
|
||||
True
|
||||
>>> is_conservative(R.z*R.j)
|
||||
False
|
||||
|
||||
"""
|
||||
|
||||
# Field is conservative irrespective of system
|
||||
# Take the first coordinate system in the result of the
|
||||
# separate method of Vector
|
||||
if not isinstance(field, Vector):
|
||||
raise TypeError("field should be a Vector")
|
||||
if field == Vector.zero:
|
||||
return True
|
||||
return curl(field).simplify() == Vector.zero
|
||||
|
||||
|
||||
def is_solenoidal(field):
|
||||
"""
|
||||
Checks if a field is solenoidal.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
field : Vector
|
||||
The field to check for solenoidal property
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy.vector import is_solenoidal
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> is_solenoidal(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k)
|
||||
True
|
||||
>>> is_solenoidal(R.y * R.j)
|
||||
False
|
||||
|
||||
"""
|
||||
|
||||
# Field is solenoidal irrespective of system
|
||||
# Take the first coordinate system in the result of the
|
||||
# separate method in Vector
|
||||
if not isinstance(field, Vector):
|
||||
raise TypeError("field should be a Vector")
|
||||
if field == Vector.zero:
|
||||
return True
|
||||
return divergence(field).simplify() is S.Zero
|
||||
|
||||
|
||||
def scalar_potential(field, coord_sys):
|
||||
"""
|
||||
Returns the scalar potential function of a field in a given
|
||||
coordinate system (without the added integration constant).
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
field : Vector
|
||||
The vector field whose scalar potential function is to be
|
||||
calculated
|
||||
|
||||
coord_sys : CoordSys3D
|
||||
The coordinate system to do the calculation in
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy.vector import scalar_potential, gradient
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> scalar_potential(R.k, R) == R.z
|
||||
True
|
||||
>>> scalar_field = 2*R.x**2*R.y*R.z
|
||||
>>> grad_field = gradient(scalar_field)
|
||||
>>> scalar_potential(grad_field, R)
|
||||
2*R.x**2*R.y*R.z
|
||||
|
||||
"""
|
||||
|
||||
# Check whether field is conservative
|
||||
if not is_conservative(field):
|
||||
raise ValueError("Field is not conservative")
|
||||
if field == Vector.zero:
|
||||
return S.Zero
|
||||
# Express the field exntirely in coord_sys
|
||||
# Substitute coordinate variables also
|
||||
if not isinstance(coord_sys, CoordSys3D):
|
||||
raise TypeError("coord_sys must be a CoordSys3D")
|
||||
field = express(field, coord_sys, variables=True)
|
||||
dimensions = coord_sys.base_vectors()
|
||||
scalars = coord_sys.base_scalars()
|
||||
# Calculate scalar potential function
|
||||
temp_function = integrate(field.dot(dimensions[0]), scalars[0])
|
||||
for i, dim in enumerate(dimensions[1:]):
|
||||
partial_diff = diff(temp_function, scalars[i + 1])
|
||||
partial_diff = field.dot(dim) - partial_diff
|
||||
temp_function += integrate(partial_diff, scalars[i + 1])
|
||||
return temp_function
|
||||
|
||||
|
||||
def scalar_potential_difference(field, coord_sys, point1, point2):
|
||||
"""
|
||||
Returns the scalar potential difference between two points in a
|
||||
certain coordinate system, wrt a given field.
|
||||
|
||||
If a scalar field is provided, its values at the two points are
|
||||
considered. If a conservative vector field is provided, the values
|
||||
of its scalar potential function at the two points are used.
|
||||
|
||||
Returns (potential at point2) - (potential at point1)
|
||||
|
||||
The position vectors of the two Points are calculated wrt the
|
||||
origin of the coordinate system provided.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
field : Vector/Expr
|
||||
The field to calculate wrt
|
||||
|
||||
coord_sys : CoordSys3D
|
||||
The coordinate system to do the calculations in
|
||||
|
||||
point1 : Point
|
||||
The initial Point in given coordinate system
|
||||
|
||||
position2 : Point
|
||||
The second Point in the given coordinate system
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy.vector import scalar_potential_difference
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> P = R.origin.locate_new('P', R.x*R.i + R.y*R.j + R.z*R.k)
|
||||
>>> vectfield = 4*R.x*R.y*R.i + 2*R.x**2*R.j
|
||||
>>> scalar_potential_difference(vectfield, R, R.origin, P)
|
||||
2*R.x**2*R.y
|
||||
>>> Q = R.origin.locate_new('O', 3*R.i + R.j + 2*R.k)
|
||||
>>> scalar_potential_difference(vectfield, R, P, Q)
|
||||
-2*R.x**2*R.y + 18
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(coord_sys, CoordSys3D):
|
||||
raise TypeError("coord_sys must be a CoordSys3D")
|
||||
if isinstance(field, Vector):
|
||||
# Get the scalar potential function
|
||||
scalar_fn = scalar_potential(field, coord_sys)
|
||||
else:
|
||||
# Field is a scalar
|
||||
scalar_fn = field
|
||||
# Express positions in required coordinate system
|
||||
origin = coord_sys.origin
|
||||
position1 = express(point1.position_wrt(origin), coord_sys,
|
||||
variables=True)
|
||||
position2 = express(point2.position_wrt(origin), coord_sys,
|
||||
variables=True)
|
||||
# Get the two positions as substitution dicts for coordinate variables
|
||||
subs_dict1 = {}
|
||||
subs_dict2 = {}
|
||||
scalars = coord_sys.base_scalars()
|
||||
for i, x in enumerate(coord_sys.base_vectors()):
|
||||
subs_dict1[scalars[i]] = x.dot(position1)
|
||||
subs_dict2[scalars[i]] = x.dot(position2)
|
||||
return scalar_fn.subs(subs_dict2) - scalar_fn.subs(subs_dict1)
|
||||
|
||||
|
||||
def matrix_to_vector(matrix, system):
|
||||
"""
|
||||
Converts a vector in matrix form to a Vector instance.
|
||||
|
||||
It is assumed that the elements of the Matrix represent the
|
||||
measure numbers of the components of the vector along basis
|
||||
vectors of 'system'.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
matrix : SymPy Matrix, Dimensions: (3, 1)
|
||||
The matrix to be converted to a vector
|
||||
|
||||
system : CoordSys3D
|
||||
The coordinate system the vector is to be defined in
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import ImmutableMatrix as Matrix
|
||||
>>> m = Matrix([1, 2, 3])
|
||||
>>> from sympy.vector import CoordSys3D, matrix_to_vector
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> v = matrix_to_vector(m, C)
|
||||
>>> v
|
||||
C.i + 2*C.j + 3*C.k
|
||||
>>> v.to_matrix(C) == m
|
||||
True
|
||||
|
||||
"""
|
||||
|
||||
outvec = Vector.zero
|
||||
vects = system.base_vectors()
|
||||
for i, x in enumerate(matrix):
|
||||
outvec += x * vects[i]
|
||||
return outvec
|
||||
|
||||
|
||||
def _path(from_object, to_object):
|
||||
"""
|
||||
Calculates the 'path' of objects starting from 'from_object'
|
||||
to 'to_object', along with the index of the first common
|
||||
ancestor in the tree.
|
||||
|
||||
Returns (index, list) tuple.
|
||||
"""
|
||||
|
||||
if from_object._root != to_object._root:
|
||||
raise ValueError("No connecting path found between " +
|
||||
str(from_object) + " and " + str(to_object))
|
||||
|
||||
other_path = []
|
||||
obj = to_object
|
||||
while obj._parent is not None:
|
||||
other_path.append(obj)
|
||||
obj = obj._parent
|
||||
other_path.append(obj)
|
||||
object_set = set(other_path)
|
||||
from_path = []
|
||||
obj = from_object
|
||||
while obj not in object_set:
|
||||
from_path.append(obj)
|
||||
obj = obj._parent
|
||||
index = len(from_path)
|
||||
from_path.extend(other_path[other_path.index(obj)::-1])
|
||||
return index, from_path
|
||||
|
||||
|
||||
def orthogonalize(*vlist, orthonormal=False):
|
||||
"""
|
||||
Takes a sequence of independent vectors and orthogonalizes them
|
||||
using the Gram - Schmidt process. Returns a list of
|
||||
orthogonal or orthonormal vectors.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
vlist : sequence of independent vectors to be made orthogonal.
|
||||
|
||||
orthonormal : Optional parameter
|
||||
Set to True if the vectors returned should be
|
||||
orthonormal.
|
||||
Default: False
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector.coordsysrect import CoordSys3D
|
||||
>>> from sympy.vector.functions import orthogonalize
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> i, j, k = C.base_vectors()
|
||||
>>> v1 = i + 2*j
|
||||
>>> v2 = 2*i + 3*j
|
||||
>>> orthogonalize(v1, v2)
|
||||
[C.i + 2*C.j, 2/5*C.i + (-1/5)*C.j]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Gram-Schmidt_process
|
||||
|
||||
"""
|
||||
|
||||
if not all(isinstance(vec, Vector) for vec in vlist):
|
||||
raise TypeError('Each element must be of Type Vector')
|
||||
|
||||
ortho_vlist = []
|
||||
for i, term in enumerate(vlist):
|
||||
for j in range(i):
|
||||
term -= ortho_vlist[j].projection(vlist[i])
|
||||
# TODO : The following line introduces a performance issue
|
||||
# and needs to be changed once a good solution for issue #10279 is
|
||||
# found.
|
||||
if term.equals(Vector.zero):
|
||||
raise ValueError("Vector set not linearly independent")
|
||||
ortho_vlist.append(term)
|
||||
|
||||
if orthonormal:
|
||||
ortho_vlist = [vec.normalize() for vec in ortho_vlist]
|
||||
|
||||
return ortho_vlist
|
||||
@@ -0,0 +1,506 @@
|
||||
from sympy.core.numbers import Rational
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.complexes import sign
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.polys.polytools import gcd
|
||||
from sympy.sets.sets import Complement
|
||||
from sympy.core import Basic, Tuple, diff, expand, Eq, Integer
|
||||
from sympy.core.sorting import ordered
|
||||
from sympy.core.symbol import _symbol
|
||||
from sympy.solvers import solveset, nonlinsolve, diophantine
|
||||
from sympy.polys import total_degree
|
||||
from sympy.geometry import Point
|
||||
from sympy.ntheory.factor_ import core
|
||||
|
||||
|
||||
class ImplicitRegion(Basic):
|
||||
"""
|
||||
Represents an implicit region in space.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Eq
|
||||
>>> from sympy.abc import x, y, z, t
|
||||
>>> from sympy.vector import ImplicitRegion
|
||||
|
||||
>>> ImplicitRegion((x, y), x**2 + y**2 - 4)
|
||||
ImplicitRegion((x, y), x**2 + y**2 - 4)
|
||||
>>> ImplicitRegion((x, y), Eq(y*x, 1))
|
||||
ImplicitRegion((x, y), x*y - 1)
|
||||
|
||||
>>> parabola = ImplicitRegion((x, y), y**2 - 4*x)
|
||||
>>> parabola.degree
|
||||
2
|
||||
>>> parabola.equation
|
||||
-4*x + y**2
|
||||
>>> parabola.rational_parametrization(t)
|
||||
(4/t**2, 4/t)
|
||||
|
||||
>>> r = ImplicitRegion((x, y, z), Eq(z, x**2 + y**2))
|
||||
>>> r.variables
|
||||
(x, y, z)
|
||||
>>> r.singular_points()
|
||||
EmptySet
|
||||
>>> r.regular_point()
|
||||
(-10, -10, 200)
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
variables : tuple to map variables in implicit equation to base scalars.
|
||||
|
||||
equation : An expression or Eq denoting the implicit equation of the region.
|
||||
|
||||
"""
|
||||
def __new__(cls, variables, equation):
|
||||
if not isinstance(variables, Tuple):
|
||||
variables = Tuple(*variables)
|
||||
|
||||
if isinstance(equation, Eq):
|
||||
equation = equation.lhs - equation.rhs
|
||||
|
||||
return super().__new__(cls, variables, equation)
|
||||
|
||||
@property
|
||||
def variables(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def equation(self):
|
||||
return self.args[1]
|
||||
|
||||
@property
|
||||
def degree(self):
|
||||
return total_degree(self.equation)
|
||||
|
||||
def regular_point(self):
|
||||
"""
|
||||
Returns a point on the implicit region.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x, y, z
|
||||
>>> from sympy.vector import ImplicitRegion
|
||||
>>> circle = ImplicitRegion((x, y), (x + 2)**2 + (y - 3)**2 - 16)
|
||||
>>> circle.regular_point()
|
||||
(-2, -1)
|
||||
>>> parabola = ImplicitRegion((x, y), x**2 - 4*y)
|
||||
>>> parabola.regular_point()
|
||||
(0, 0)
|
||||
>>> r = ImplicitRegion((x, y, z), (x + y + z)**4)
|
||||
>>> r.regular_point()
|
||||
(-10, -10, 20)
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
- Erik Hillgarter, "Rational Points on Conics", Diploma Thesis, RISC-Linz,
|
||||
J. Kepler Universitat Linz, 1996. Available:
|
||||
https://www3.risc.jku.at/publications/download/risc_1355/Rational%20Points%20on%20Conics.pdf
|
||||
|
||||
"""
|
||||
equation = self.equation
|
||||
|
||||
if len(self.variables) == 1:
|
||||
return (list(solveset(equation, self.variables[0], domain=S.Reals))[0],)
|
||||
elif len(self.variables) == 2:
|
||||
|
||||
if self.degree == 2:
|
||||
coeffs = a, b, c, d, e, f = conic_coeff(self.variables, equation)
|
||||
|
||||
if b**2 == 4*a*c:
|
||||
x_reg, y_reg = self._regular_point_parabola(*coeffs)
|
||||
else:
|
||||
x_reg, y_reg = self._regular_point_ellipse(*coeffs)
|
||||
return x_reg, y_reg
|
||||
|
||||
if len(self.variables) == 3:
|
||||
x, y, z = self.variables
|
||||
|
||||
for x_reg in range(-10, 10):
|
||||
for y_reg in range(-10, 10):
|
||||
if not solveset(equation.subs({x: x_reg, y: y_reg}), self.variables[2], domain=S.Reals).is_empty:
|
||||
return (x_reg, y_reg, list(solveset(equation.subs({x: x_reg, y: y_reg})))[0])
|
||||
|
||||
if len(self.singular_points()) != 0:
|
||||
return list[self.singular_points()][0]
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def _regular_point_parabola(self, a, b, c, d, e, f):
|
||||
ok = (a, d) != (0, 0) and (c, e) != (0, 0) and b**2 == 4*a*c and (a, c) != (0, 0)
|
||||
|
||||
if not ok:
|
||||
raise ValueError("Rational Point on the conic does not exist")
|
||||
|
||||
if a != 0:
|
||||
d_dash, f_dash = (4*a*e - 2*b*d, 4*a*f - d**2)
|
||||
if d_dash != 0:
|
||||
y_reg = -f_dash/d_dash
|
||||
x_reg = -(d + b*y_reg)/(2*a)
|
||||
else:
|
||||
ok = False
|
||||
elif c != 0:
|
||||
d_dash, f_dash = (4*c*d - 2*b*e, 4*c*f - e**2)
|
||||
if d_dash != 0:
|
||||
x_reg = -f_dash/d_dash
|
||||
y_reg = -(e + b*x_reg)/(2*c)
|
||||
else:
|
||||
ok = False
|
||||
|
||||
if ok:
|
||||
return x_reg, y_reg
|
||||
else:
|
||||
raise ValueError("Rational Point on the conic does not exist")
|
||||
|
||||
def _regular_point_ellipse(self, a, b, c, d, e, f):
|
||||
D = 4*a*c - b**2
|
||||
ok = D
|
||||
|
||||
if not ok:
|
||||
raise ValueError("Rational Point on the conic does not exist")
|
||||
|
||||
if a == 0 and c == 0:
|
||||
K = -1
|
||||
L = 4*(d*e - b*f)
|
||||
elif c != 0:
|
||||
K = D
|
||||
L = 4*c**2*d**2 - 4*b*c*d*e + 4*a*c*e**2 + 4*b**2*c*f - 16*a*c**2*f
|
||||
else:
|
||||
K = D
|
||||
L = 4*a**2*e**2 - 4*b*a*d*e + 4*b**2*a*f
|
||||
|
||||
ok = L != 0 and not(K > 0 and L < 0)
|
||||
if not ok:
|
||||
raise ValueError("Rational Point on the conic does not exist")
|
||||
|
||||
K = Rational(K).limit_denominator(10**12)
|
||||
L = Rational(L).limit_denominator(10**12)
|
||||
|
||||
k1, k2 = K.p, K.q
|
||||
l1, l2 = L.p, L.q
|
||||
g = gcd(k2, l2)
|
||||
|
||||
a1 = (l2*k2)/g
|
||||
b1 = (k1*l2)/g
|
||||
c1 = -(l1*k2)/g
|
||||
a2 = sign(a1)*core(abs(a1), 2)
|
||||
r1 = sqrt(a1/a2)
|
||||
b2 = sign(b1)*core(abs(b1), 2)
|
||||
r2 = sqrt(b1/b2)
|
||||
c2 = sign(c1)*core(abs(c1), 2)
|
||||
r3 = sqrt(c1/c2)
|
||||
|
||||
g = gcd(gcd(a2, b2), c2)
|
||||
a2 = a2/g
|
||||
b2 = b2/g
|
||||
c2 = c2/g
|
||||
|
||||
g1 = gcd(a2, b2)
|
||||
a2 = a2/g1
|
||||
b2 = b2/g1
|
||||
c2 = c2*g1
|
||||
|
||||
g2 = gcd(a2,c2)
|
||||
a2 = a2/g2
|
||||
b2 = b2*g2
|
||||
c2 = c2/g2
|
||||
|
||||
g3 = gcd(b2, c2)
|
||||
a2 = a2*g3
|
||||
b2 = b2/g3
|
||||
c2 = c2/g3
|
||||
|
||||
x, y, z = symbols("x y z")
|
||||
eq = a2*x**2 + b2*y**2 + c2*z**2
|
||||
|
||||
solutions = diophantine(eq)
|
||||
|
||||
if len(solutions) == 0:
|
||||
raise ValueError("Rational Point on the conic does not exist")
|
||||
|
||||
flag = False
|
||||
for sol in solutions:
|
||||
syms = Tuple(*sol).free_symbols
|
||||
rep = dict.fromkeys(syms, 3)
|
||||
sol_z = sol[2]
|
||||
|
||||
if sol_z == 0:
|
||||
flag = True
|
||||
continue
|
||||
|
||||
if not isinstance(sol_z, (int, Integer)):
|
||||
syms_z = sol_z.free_symbols
|
||||
|
||||
if len(syms_z) == 1:
|
||||
p = next(iter(syms_z))
|
||||
p_values = Complement(S.Integers, solveset(Eq(sol_z, 0), p, S.Integers))
|
||||
rep[p] = next(iter(p_values))
|
||||
|
||||
if len(syms_z) == 2:
|
||||
p, q = list(ordered(syms_z))
|
||||
|
||||
for i in S.Integers:
|
||||
subs_sol_z = sol_z.subs(p, i)
|
||||
q_values = Complement(S.Integers, solveset(Eq(subs_sol_z, 0), q, S.Integers))
|
||||
|
||||
if not q_values.is_empty:
|
||||
rep[p] = i
|
||||
rep[q] = next(iter(q_values))
|
||||
break
|
||||
|
||||
if len(syms) != 0:
|
||||
x, y, z = tuple(s.subs(rep) for s in sol)
|
||||
else:
|
||||
x, y, z = sol
|
||||
flag = False
|
||||
break
|
||||
|
||||
if flag:
|
||||
raise ValueError("Rational Point on the conic does not exist")
|
||||
|
||||
x = (x*g3)/r1
|
||||
y = (y*g2)/r2
|
||||
z = (z*g1)/r3
|
||||
x = x/z
|
||||
y = y/z
|
||||
|
||||
if a == 0 and c == 0:
|
||||
x_reg = (x + y - 2*e)/(2*b)
|
||||
y_reg = (x - y - 2*d)/(2*b)
|
||||
elif c != 0:
|
||||
x_reg = (x - 2*d*c + b*e)/K
|
||||
y_reg = (y - b*x_reg - e)/(2*c)
|
||||
else:
|
||||
y_reg = (x - 2*e*a + b*d)/K
|
||||
x_reg = (y - b*y_reg - d)/(2*a)
|
||||
|
||||
return x_reg, y_reg
|
||||
|
||||
def singular_points(self):
|
||||
"""
|
||||
Returns a set of singular points of the region.
|
||||
|
||||
The singular points are those points on the region
|
||||
where all partial derivatives vanish.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x, y
|
||||
>>> from sympy.vector import ImplicitRegion
|
||||
>>> I = ImplicitRegion((x, y), (y-1)**2 -x**3 + 2*x**2 -x)
|
||||
>>> I.singular_points()
|
||||
{(1, 1)}
|
||||
|
||||
"""
|
||||
eq_list = [self.equation]
|
||||
for var in self.variables:
|
||||
eq_list += [diff(self.equation, var)]
|
||||
|
||||
return nonlinsolve(eq_list, list(self.variables))
|
||||
|
||||
def multiplicity(self, point):
|
||||
"""
|
||||
Returns the multiplicity of a singular point on the region.
|
||||
|
||||
A singular point (x,y) of region is said to be of multiplicity m
|
||||
if all the partial derivatives off to order m - 1 vanish there.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x, y, z
|
||||
>>> from sympy.vector import ImplicitRegion
|
||||
>>> I = ImplicitRegion((x, y, z), x**2 + y**3 - z**4)
|
||||
>>> I.singular_points()
|
||||
{(0, 0, 0)}
|
||||
>>> I.multiplicity((0, 0, 0))
|
||||
2
|
||||
|
||||
"""
|
||||
if isinstance(point, Point):
|
||||
point = point.args
|
||||
|
||||
modified_eq = self.equation
|
||||
|
||||
for i, var in enumerate(self.variables):
|
||||
modified_eq = modified_eq.subs(var, var + point[i])
|
||||
modified_eq = expand(modified_eq)
|
||||
|
||||
if len(modified_eq.args) != 0:
|
||||
terms = modified_eq.args
|
||||
m = min(total_degree(term) for term in terms)
|
||||
else:
|
||||
terms = modified_eq
|
||||
m = total_degree(terms)
|
||||
|
||||
return m
|
||||
|
||||
def rational_parametrization(self, parameters=('t', 's'), reg_point=None):
|
||||
"""
|
||||
Returns the rational parametrization of implicit region.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Eq
|
||||
>>> from sympy.abc import x, y, z, s, t
|
||||
>>> from sympy.vector import ImplicitRegion
|
||||
|
||||
>>> parabola = ImplicitRegion((x, y), y**2 - 4*x)
|
||||
>>> parabola.rational_parametrization()
|
||||
(4/t**2, 4/t)
|
||||
|
||||
>>> circle = ImplicitRegion((x, y), Eq(x**2 + y**2, 4))
|
||||
>>> circle.rational_parametrization()
|
||||
(4*t/(t**2 + 1), 4*t**2/(t**2 + 1) - 2)
|
||||
|
||||
>>> I = ImplicitRegion((x, y), x**3 + x**2 - y**2)
|
||||
>>> I.rational_parametrization()
|
||||
(t**2 - 1, t*(t**2 - 1))
|
||||
|
||||
>>> cubic_curve = ImplicitRegion((x, y), x**3 + x**2 - y**2)
|
||||
>>> cubic_curve.rational_parametrization(parameters=(t))
|
||||
(t**2 - 1, t*(t**2 - 1))
|
||||
|
||||
>>> sphere = ImplicitRegion((x, y, z), x**2 + y**2 + z**2 - 4)
|
||||
>>> sphere.rational_parametrization(parameters=(t, s))
|
||||
(-2 + 4/(s**2 + t**2 + 1), 4*s/(s**2 + t**2 + 1), 4*t/(s**2 + t**2 + 1))
|
||||
|
||||
For some conics, regular_points() is unable to find a point on curve.
|
||||
To calulcate the parametric representation in such cases, user need
|
||||
to determine a point on the region and pass it using reg_point.
|
||||
|
||||
>>> c = ImplicitRegion((x, y), (x - 1/2)**2 + (y)**2 - (1/4)**2)
|
||||
>>> c.rational_parametrization(reg_point=(3/4, 0))
|
||||
(0.75 - 0.5/(t**2 + 1), -0.5*t/(t**2 + 1))
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
- Christoph M. Hoffmann, "Conversion Methods between Parametric and
|
||||
Implicit Curves and Surfaces", Purdue e-Pubs, 1990. Available:
|
||||
https://docs.lib.purdue.edu/cgi/viewcontent.cgi?article=1827&context=cstech
|
||||
|
||||
"""
|
||||
equation = self.equation
|
||||
degree = self.degree
|
||||
|
||||
if degree == 1:
|
||||
if len(self.variables) == 1:
|
||||
return (equation,)
|
||||
elif len(self.variables) == 2:
|
||||
x, y = self.variables
|
||||
y_par = list(solveset(equation, y))[0]
|
||||
return x, y_par
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
point = ()
|
||||
|
||||
# Finding the (n - 1) fold point of the monoid of degree
|
||||
if degree == 2:
|
||||
# For degree 2 curves, either a regular point or a singular point can be used.
|
||||
if reg_point is not None:
|
||||
# Using point provided by the user as regular point
|
||||
point = reg_point
|
||||
else:
|
||||
if len(self.singular_points()) != 0:
|
||||
point = list(self.singular_points())[0]
|
||||
else:
|
||||
point = self.regular_point()
|
||||
|
||||
if len(self.singular_points()) != 0:
|
||||
singular_points = self.singular_points()
|
||||
for spoint in singular_points:
|
||||
syms = Tuple(*spoint).free_symbols
|
||||
rep = dict.fromkeys(syms, 2)
|
||||
|
||||
if len(syms) != 0:
|
||||
spoint = tuple(s.subs(rep) for s in spoint)
|
||||
|
||||
if self.multiplicity(spoint) == degree - 1:
|
||||
point = spoint
|
||||
break
|
||||
|
||||
if len(point) == 0:
|
||||
# The region in not a monoid
|
||||
raise NotImplementedError()
|
||||
|
||||
modified_eq = equation
|
||||
|
||||
# Shifting the region such that fold point moves to origin
|
||||
for i, var in enumerate(self.variables):
|
||||
modified_eq = modified_eq.subs(var, var + point[i])
|
||||
modified_eq = expand(modified_eq)
|
||||
|
||||
hn = hn_1 = 0
|
||||
for term in modified_eq.args:
|
||||
if total_degree(term) == degree:
|
||||
hn += term
|
||||
else:
|
||||
hn_1 += term
|
||||
|
||||
hn_1 = -1*hn_1
|
||||
|
||||
if not isinstance(parameters, tuple):
|
||||
parameters = (parameters,)
|
||||
|
||||
if len(self.variables) == 2:
|
||||
|
||||
parameter1 = parameters[0]
|
||||
if parameter1 == 's':
|
||||
# To avoid name conflict between parameters
|
||||
s = _symbol('s_', real=True)
|
||||
else:
|
||||
s = _symbol('s', real=True)
|
||||
t = _symbol(parameter1, real=True)
|
||||
|
||||
hn = hn.subs({self.variables[0]: s, self.variables[1]: t})
|
||||
hn_1 = hn_1.subs({self.variables[0]: s, self.variables[1]: t})
|
||||
|
||||
x_par = (s*(hn_1/hn)).subs(s, 1) + point[0]
|
||||
y_par = (t*(hn_1/hn)).subs(s, 1) + point[1]
|
||||
|
||||
return x_par, y_par
|
||||
|
||||
elif len(self.variables) == 3:
|
||||
|
||||
parameter1, parameter2 = parameters
|
||||
if 'r' in parameters:
|
||||
# To avoid name conflict between parameters
|
||||
r = _symbol('r_', real=True)
|
||||
else:
|
||||
r = _symbol('r', real=True)
|
||||
s = _symbol(parameter2, real=True)
|
||||
t = _symbol(parameter1, real=True)
|
||||
|
||||
hn = hn.subs({self.variables[0]: r, self.variables[1]: s, self.variables[2]: t})
|
||||
hn_1 = hn_1.subs({self.variables[0]: r, self.variables[1]: s, self.variables[2]: t})
|
||||
|
||||
x_par = (r*(hn_1/hn)).subs(r, 1) + point[0]
|
||||
y_par = (s*(hn_1/hn)).subs(r, 1) + point[1]
|
||||
z_par = (t*(hn_1/hn)).subs(r, 1) + point[2]
|
||||
|
||||
return x_par, y_par, z_par
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
def conic_coeff(variables, equation):
|
||||
if total_degree(equation) != 2:
|
||||
raise ValueError()
|
||||
x = variables[0]
|
||||
y = variables[1]
|
||||
|
||||
equation = expand(equation)
|
||||
a = equation.coeff(x**2)
|
||||
b = equation.coeff(x*y)
|
||||
c = equation.coeff(y**2)
|
||||
d = equation.coeff(x, 1).coeff(y, 0)
|
||||
e = equation.coeff(y, 1).coeff(x, 0)
|
||||
f = equation.coeff(x, 0).coeff(y, 0)
|
||||
return a, b, c, d, e, f
|
||||
@@ -0,0 +1,206 @@
|
||||
from sympy.core import Basic, diff
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.sorting import default_sort_key
|
||||
from sympy.matrices import Matrix
|
||||
from sympy.integrals import Integral, integrate
|
||||
from sympy.geometry.entity import GeometryEntity
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.utilities.iterables import topological_sort
|
||||
from sympy.vector import (CoordSys3D, Vector, ParametricRegion,
|
||||
parametric_region_list, ImplicitRegion)
|
||||
from sympy.vector.operators import _get_coord_systems
|
||||
|
||||
|
||||
class ParametricIntegral(Basic):
|
||||
"""
|
||||
Represents integral of a scalar or vector field
|
||||
over a Parametric Region
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import cos, sin, pi
|
||||
>>> from sympy.vector import CoordSys3D, ParametricRegion, ParametricIntegral
|
||||
>>> from sympy.abc import r, t, theta, phi
|
||||
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> curve = ParametricRegion((3*t - 2, t + 1), (t, 1, 2))
|
||||
>>> ParametricIntegral(C.x, curve)
|
||||
5*sqrt(10)/2
|
||||
>>> length = ParametricIntegral(1, curve)
|
||||
>>> length
|
||||
sqrt(10)
|
||||
>>> semisphere = ParametricRegion((2*sin(phi)*cos(theta), 2*sin(phi)*sin(theta), 2*cos(phi)),\
|
||||
(theta, 0, 2*pi), (phi, 0, pi/2))
|
||||
>>> ParametricIntegral(C.z, semisphere)
|
||||
8*pi
|
||||
|
||||
>>> ParametricIntegral(C.j + C.k, ParametricRegion((r*cos(theta), r*sin(theta)), r, theta))
|
||||
0
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, field, parametricregion):
|
||||
|
||||
coord_set = _get_coord_systems(field)
|
||||
|
||||
if len(coord_set) == 0:
|
||||
coord_sys = CoordSys3D('C')
|
||||
elif len(coord_set) > 1:
|
||||
raise ValueError
|
||||
else:
|
||||
coord_sys = next(iter(coord_set))
|
||||
|
||||
if parametricregion.dimensions == 0:
|
||||
return S.Zero
|
||||
|
||||
base_vectors = coord_sys.base_vectors()
|
||||
base_scalars = coord_sys.base_scalars()
|
||||
|
||||
parametricfield = field
|
||||
|
||||
r = Vector.zero
|
||||
for i in range(len(parametricregion.definition)):
|
||||
r += base_vectors[i]*parametricregion.definition[i]
|
||||
|
||||
if len(coord_set) != 0:
|
||||
for i in range(len(parametricregion.definition)):
|
||||
parametricfield = parametricfield.subs(base_scalars[i], parametricregion.definition[i])
|
||||
|
||||
if parametricregion.dimensions == 1:
|
||||
parameter = parametricregion.parameters[0]
|
||||
|
||||
r_diff = diff(r, parameter)
|
||||
lower, upper = parametricregion.limits[parameter][0], parametricregion.limits[parameter][1]
|
||||
|
||||
if isinstance(parametricfield, Vector):
|
||||
integrand = simplify(r_diff.dot(parametricfield))
|
||||
else:
|
||||
integrand = simplify(r_diff.magnitude()*parametricfield)
|
||||
|
||||
result = integrate(integrand, (parameter, lower, upper))
|
||||
|
||||
elif parametricregion.dimensions == 2:
|
||||
u, v = cls._bounds_case(parametricregion.parameters, parametricregion.limits)
|
||||
|
||||
r_u = diff(r, u)
|
||||
r_v = diff(r, v)
|
||||
normal_vector = simplify(r_u.cross(r_v))
|
||||
|
||||
if isinstance(parametricfield, Vector):
|
||||
integrand = parametricfield.dot(normal_vector)
|
||||
else:
|
||||
integrand = parametricfield*normal_vector.magnitude()
|
||||
|
||||
integrand = simplify(integrand)
|
||||
|
||||
lower_u, upper_u = parametricregion.limits[u][0], parametricregion.limits[u][1]
|
||||
lower_v, upper_v = parametricregion.limits[v][0], parametricregion.limits[v][1]
|
||||
|
||||
result = integrate(integrand, (u, lower_u, upper_u), (v, lower_v, upper_v))
|
||||
|
||||
else:
|
||||
variables = cls._bounds_case(parametricregion.parameters, parametricregion.limits)
|
||||
coeff = Matrix(parametricregion.definition).jacobian(variables).det()
|
||||
integrand = simplify(parametricfield*coeff)
|
||||
|
||||
l = [(var, parametricregion.limits[var][0], parametricregion.limits[var][1]) for var in variables]
|
||||
result = integrate(integrand, *l)
|
||||
|
||||
if not isinstance(result, Integral):
|
||||
return result
|
||||
else:
|
||||
return super().__new__(cls, field, parametricregion)
|
||||
|
||||
@classmethod
|
||||
def _bounds_case(cls, parameters, limits):
|
||||
|
||||
V = list(limits.keys())
|
||||
E = []
|
||||
|
||||
for p in V:
|
||||
lower_p = limits[p][0]
|
||||
upper_p = limits[p][1]
|
||||
|
||||
lower_p = lower_p.atoms()
|
||||
upper_p = upper_p.atoms()
|
||||
E.extend((p, q) for q in V if p != q and
|
||||
(lower_p.issuperset({q}) or upper_p.issuperset({q})))
|
||||
|
||||
if not E:
|
||||
return parameters
|
||||
else:
|
||||
return topological_sort((V, E), key=default_sort_key)
|
||||
|
||||
@property
|
||||
def field(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def parametricregion(self):
|
||||
return self.args[1]
|
||||
|
||||
|
||||
def vector_integrate(field, *region):
|
||||
"""
|
||||
Compute the integral of a vector/scalar field
|
||||
over a a region or a set of parameters.
|
||||
|
||||
Examples
|
||||
========
|
||||
>>> from sympy.vector import CoordSys3D, ParametricRegion, vector_integrate
|
||||
>>> from sympy.abc import x, y, t
|
||||
>>> C = CoordSys3D('C')
|
||||
|
||||
>>> region = ParametricRegion((t, t**2), (t, 1, 5))
|
||||
>>> vector_integrate(C.x*C.i, region)
|
||||
12
|
||||
|
||||
Integrals over some objects of geometry module can also be calculated.
|
||||
|
||||
>>> from sympy.geometry import Point, Circle, Triangle
|
||||
>>> c = Circle(Point(0, 2), 5)
|
||||
>>> vector_integrate(C.x**2 + C.y**2, c)
|
||||
290*pi
|
||||
>>> triangle = Triangle(Point(-2, 3), Point(2, 3), Point(0, 5))
|
||||
>>> vector_integrate(3*C.x**2*C.y*C.i + C.j, triangle)
|
||||
-8
|
||||
|
||||
Integrals over some simple implicit regions can be computed. But in most cases,
|
||||
it takes too long to compute over them. This is due to the expressions of parametric
|
||||
representation becoming large.
|
||||
|
||||
>>> from sympy.vector import ImplicitRegion
|
||||
>>> c2 = ImplicitRegion((x, y), (x - 2)**2 + (y - 1)**2 - 9)
|
||||
>>> vector_integrate(1, c2)
|
||||
6*pi
|
||||
|
||||
Integral of fields with respect to base scalars:
|
||||
|
||||
>>> vector_integrate(12*C.y**3, (C.y, 1, 3))
|
||||
240
|
||||
>>> vector_integrate(C.x**2*C.z, C.x)
|
||||
C.x**3*C.z/3
|
||||
>>> vector_integrate(C.x*C.i - C.y*C.k, C.x)
|
||||
(Integral(C.x, C.x))*C.i + (Integral(-C.y, C.x))*C.k
|
||||
>>> _.doit()
|
||||
C.x**2/2*C.i + (-C.x*C.y)*C.k
|
||||
|
||||
"""
|
||||
if len(region) == 1:
|
||||
if isinstance(region[0], ParametricRegion):
|
||||
return ParametricIntegral(field, region[0])
|
||||
|
||||
if isinstance(region[0], ImplicitRegion):
|
||||
region = parametric_region_list(region[0])[0]
|
||||
return vector_integrate(field, region)
|
||||
|
||||
if isinstance(region[0], GeometryEntity):
|
||||
regions_list = parametric_region_list(region[0])
|
||||
|
||||
result = 0
|
||||
for reg in regions_list:
|
||||
result += vector_integrate(field, reg)
|
||||
return result
|
||||
|
||||
return integrate(field, *region)
|
||||
@@ -0,0 +1,67 @@
|
||||
#sympy.vector.kind
|
||||
|
||||
from sympy.core.kind import Kind, _NumberKind, NumberKind
|
||||
from sympy.core.mul import Mul
|
||||
|
||||
class VectorKind(Kind):
|
||||
"""
|
||||
Kind for all vector objects in SymPy.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
element_kind : Kind
|
||||
Kind of the element. Default is
|
||||
:class:`sympy.core.kind.NumberKind`,
|
||||
which means that the vector contains only numbers.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
Any instance of Vector class has kind ``VectorKind``:
|
||||
|
||||
>>> from sympy.vector.coordsysrect import CoordSys3D
|
||||
>>> Sys = CoordSys3D('Sys')
|
||||
>>> Sys.i.kind
|
||||
VectorKind(NumberKind)
|
||||
|
||||
Operations between instances of Vector keep also have the kind ``VectorKind``:
|
||||
|
||||
>>> from sympy.core.add import Add
|
||||
>>> v1 = Sys.i * 2 + Sys.j * 3 + Sys.k * 4
|
||||
>>> v2 = Sys.i * Sys.x + Sys.j * Sys.y + Sys.k * Sys.z
|
||||
>>> v1.kind
|
||||
VectorKind(NumberKind)
|
||||
>>> v2.kind
|
||||
VectorKind(NumberKind)
|
||||
>>> Add(v1, v2).kind
|
||||
VectorKind(NumberKind)
|
||||
|
||||
Subclasses of Vector also have the kind ``VectorKind``, such as
|
||||
Cross, VectorAdd, VectorMul or VectorZero.
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.core.kind.Kind
|
||||
sympy.matrices.kind.MatrixKind
|
||||
|
||||
"""
|
||||
def __new__(cls, element_kind=NumberKind):
|
||||
obj = super().__new__(cls, element_kind)
|
||||
obj.element_kind = element_kind
|
||||
return obj
|
||||
|
||||
def __repr__(self):
|
||||
return "VectorKind(%s)" % self.element_kind
|
||||
|
||||
@Mul._kind_dispatcher.register(_NumberKind, VectorKind)
|
||||
def num_vec_mul(k1, k2):
|
||||
"""
|
||||
The result of a multiplication between a number and a Vector should be of VectorKind.
|
||||
The element kind is selected by recursive dispatching.
|
||||
"""
|
||||
if not isinstance(k2, VectorKind):
|
||||
k1, k2 = k2, k1
|
||||
elemk = Mul._kind_dispatcher(k1, k2.element_kind)
|
||||
return VectorKind(elemk)
|
||||
@@ -0,0 +1,335 @@
|
||||
import collections
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core import sympify, S, preorder_traversal
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.vector import Vector, VectorMul, VectorAdd, Cross, Dot
|
||||
from sympy.core.function import Derivative
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.mul import Mul
|
||||
|
||||
|
||||
def _get_coord_systems(expr):
|
||||
g = preorder_traversal(expr)
|
||||
ret = set()
|
||||
for i in g:
|
||||
if isinstance(i, CoordSys3D):
|
||||
ret.add(i)
|
||||
g.skip()
|
||||
return frozenset(ret)
|
||||
|
||||
|
||||
def _split_mul_args_wrt_coordsys(expr):
|
||||
d = collections.defaultdict(lambda: S.One)
|
||||
for i in expr.args:
|
||||
d[_get_coord_systems(i)] *= i
|
||||
return list(d.values())
|
||||
|
||||
|
||||
class Gradient(Expr):
|
||||
"""
|
||||
Represents unevaluated Gradient.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Gradient
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> s = R.x*R.y*R.z
|
||||
>>> Gradient(s)
|
||||
Gradient(R.x*R.y*R.z)
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, expr):
|
||||
expr = sympify(expr)
|
||||
obj = Expr.__new__(cls, expr)
|
||||
obj._expr = expr
|
||||
return obj
|
||||
|
||||
def doit(self, **hints):
|
||||
return gradient(self._expr, doit=True)
|
||||
|
||||
|
||||
class Divergence(Expr):
|
||||
"""
|
||||
Represents unevaluated Divergence.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Divergence
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> v = R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k
|
||||
>>> Divergence(v)
|
||||
Divergence(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k)
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, expr):
|
||||
expr = sympify(expr)
|
||||
obj = Expr.__new__(cls, expr)
|
||||
obj._expr = expr
|
||||
return obj
|
||||
|
||||
def doit(self, **hints):
|
||||
return divergence(self._expr, doit=True)
|
||||
|
||||
|
||||
class Curl(Expr):
|
||||
"""
|
||||
Represents unevaluated Curl.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Curl
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> v = R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k
|
||||
>>> Curl(v)
|
||||
Curl(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k)
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, expr):
|
||||
expr = sympify(expr)
|
||||
obj = Expr.__new__(cls, expr)
|
||||
obj._expr = expr
|
||||
return obj
|
||||
|
||||
def doit(self, **hints):
|
||||
return curl(self._expr, doit=True)
|
||||
|
||||
|
||||
def curl(vect, doit=True):
|
||||
"""
|
||||
Returns the curl of a vector field computed wrt the base scalars
|
||||
of the given coordinate system.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
vect : Vector
|
||||
The vector operand
|
||||
|
||||
doit : bool
|
||||
If True, the result is returned after calling .doit() on
|
||||
each component. Else, the returned expression contains
|
||||
Derivative instances
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, curl
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> v1 = R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k
|
||||
>>> curl(v1)
|
||||
0
|
||||
>>> v2 = R.x*R.y*R.z*R.i
|
||||
>>> curl(v2)
|
||||
R.x*R.y*R.j + (-R.x*R.z)*R.k
|
||||
|
||||
"""
|
||||
|
||||
coord_sys = _get_coord_systems(vect)
|
||||
|
||||
if len(coord_sys) == 0:
|
||||
return Vector.zero
|
||||
elif len(coord_sys) == 1:
|
||||
coord_sys = next(iter(coord_sys))
|
||||
i, j, k = coord_sys.base_vectors()
|
||||
x, y, z = coord_sys.base_scalars()
|
||||
h1, h2, h3 = coord_sys.lame_coefficients()
|
||||
vectx = vect.dot(i)
|
||||
vecty = vect.dot(j)
|
||||
vectz = vect.dot(k)
|
||||
outvec = Vector.zero
|
||||
outvec += (Derivative(vectz * h3, y) -
|
||||
Derivative(vecty * h2, z)) * i / (h2 * h3)
|
||||
outvec += (Derivative(vectx * h1, z) -
|
||||
Derivative(vectz * h3, x)) * j / (h1 * h3)
|
||||
outvec += (Derivative(vecty * h2, x) -
|
||||
Derivative(vectx * h1, y)) * k / (h2 * h1)
|
||||
|
||||
if doit:
|
||||
return outvec.doit()
|
||||
return outvec
|
||||
else:
|
||||
if isinstance(vect, (Add, VectorAdd)):
|
||||
from sympy.vector import express
|
||||
try:
|
||||
cs = next(iter(coord_sys))
|
||||
args = [express(i, cs, variables=True) for i in vect.args]
|
||||
except ValueError:
|
||||
args = vect.args
|
||||
return VectorAdd.fromiter(curl(i, doit=doit) for i in args)
|
||||
elif isinstance(vect, (Mul, VectorMul)):
|
||||
vector = [i for i in vect.args if isinstance(i, (Vector, Cross, Gradient))][0]
|
||||
scalar = Mul.fromiter(i for i in vect.args if not isinstance(i, (Vector, Cross, Gradient)))
|
||||
res = Cross(gradient(scalar), vector).doit() + scalar*curl(vector, doit=doit)
|
||||
if doit:
|
||||
return res.doit()
|
||||
return res
|
||||
elif isinstance(vect, (Cross, Curl, Gradient)):
|
||||
return Curl(vect)
|
||||
else:
|
||||
raise ValueError("Invalid argument for curl")
|
||||
|
||||
|
||||
def divergence(vect, doit=True):
|
||||
"""
|
||||
Returns the divergence of a vector field computed wrt the base
|
||||
scalars of the given coordinate system.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
vector : Vector
|
||||
The vector operand
|
||||
|
||||
doit : bool
|
||||
If True, the result is returned after calling .doit() on
|
||||
each component. Else, the returned expression contains
|
||||
Derivative instances
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, divergence
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> v1 = R.x*R.y*R.z * (R.i+R.j+R.k)
|
||||
|
||||
>>> divergence(v1)
|
||||
R.x*R.y + R.x*R.z + R.y*R.z
|
||||
>>> v2 = 2*R.y*R.z*R.j
|
||||
>>> divergence(v2)
|
||||
2*R.z
|
||||
|
||||
"""
|
||||
coord_sys = _get_coord_systems(vect)
|
||||
if len(coord_sys) == 0:
|
||||
return S.Zero
|
||||
elif len(coord_sys) == 1:
|
||||
if isinstance(vect, (Cross, Curl, Gradient)):
|
||||
return Divergence(vect)
|
||||
# TODO: is case of many coord systems, this gets a random one:
|
||||
coord_sys = next(iter(coord_sys))
|
||||
i, j, k = coord_sys.base_vectors()
|
||||
x, y, z = coord_sys.base_scalars()
|
||||
h1, h2, h3 = coord_sys.lame_coefficients()
|
||||
vx = _diff_conditional(vect.dot(i), x, h2, h3) \
|
||||
/ (h1 * h2 * h3)
|
||||
vy = _diff_conditional(vect.dot(j), y, h3, h1) \
|
||||
/ (h1 * h2 * h3)
|
||||
vz = _diff_conditional(vect.dot(k), z, h1, h2) \
|
||||
/ (h1 * h2 * h3)
|
||||
res = vx + vy + vz
|
||||
if doit:
|
||||
return res.doit()
|
||||
return res
|
||||
else:
|
||||
if isinstance(vect, (Add, VectorAdd)):
|
||||
return Add.fromiter(divergence(i, doit=doit) for i in vect.args)
|
||||
elif isinstance(vect, (Mul, VectorMul)):
|
||||
vector = [i for i in vect.args if isinstance(i, (Vector, Cross, Gradient))][0]
|
||||
scalar = Mul.fromiter(i for i in vect.args if not isinstance(i, (Vector, Cross, Gradient)))
|
||||
res = Dot(vector, gradient(scalar)) + scalar*divergence(vector, doit=doit)
|
||||
if doit:
|
||||
return res.doit()
|
||||
return res
|
||||
elif isinstance(vect, (Cross, Curl, Gradient)):
|
||||
return Divergence(vect)
|
||||
else:
|
||||
raise ValueError("Invalid argument for divergence")
|
||||
|
||||
|
||||
def gradient(scalar_field, doit=True):
|
||||
"""
|
||||
Returns the vector gradient of a scalar field computed wrt the
|
||||
base scalars of the given coordinate system.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
scalar_field : SymPy Expr
|
||||
The scalar field to compute the gradient of
|
||||
|
||||
doit : bool
|
||||
If True, the result is returned after calling .doit() on
|
||||
each component. Else, the returned expression contains
|
||||
Derivative instances
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, gradient
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> s1 = R.x*R.y*R.z
|
||||
>>> gradient(s1)
|
||||
R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k
|
||||
>>> s2 = 5*R.x**2*R.z
|
||||
>>> gradient(s2)
|
||||
10*R.x*R.z*R.i + 5*R.x**2*R.k
|
||||
|
||||
"""
|
||||
coord_sys = _get_coord_systems(scalar_field)
|
||||
|
||||
if len(coord_sys) == 0:
|
||||
return Vector.zero
|
||||
elif len(coord_sys) == 1:
|
||||
coord_sys = next(iter(coord_sys))
|
||||
h1, h2, h3 = coord_sys.lame_coefficients()
|
||||
i, j, k = coord_sys.base_vectors()
|
||||
x, y, z = coord_sys.base_scalars()
|
||||
vx = Derivative(scalar_field, x) / h1
|
||||
vy = Derivative(scalar_field, y) / h2
|
||||
vz = Derivative(scalar_field, z) / h3
|
||||
|
||||
if doit:
|
||||
return (vx * i + vy * j + vz * k).doit()
|
||||
return vx * i + vy * j + vz * k
|
||||
else:
|
||||
if isinstance(scalar_field, (Add, VectorAdd)):
|
||||
return VectorAdd.fromiter(gradient(i) for i in scalar_field.args)
|
||||
if isinstance(scalar_field, (Mul, VectorMul)):
|
||||
s = _split_mul_args_wrt_coordsys(scalar_field)
|
||||
return VectorAdd.fromiter(scalar_field / i * gradient(i) for i in s)
|
||||
return Gradient(scalar_field)
|
||||
|
||||
|
||||
class Laplacian(Expr):
|
||||
"""
|
||||
Represents unevaluated Laplacian.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Laplacian
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> v = 3*R.x**3*R.y**2*R.z**3
|
||||
>>> Laplacian(v)
|
||||
Laplacian(3*R.x**3*R.y**2*R.z**3)
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, expr):
|
||||
expr = sympify(expr)
|
||||
obj = Expr.__new__(cls, expr)
|
||||
obj._expr = expr
|
||||
return obj
|
||||
|
||||
def doit(self, **hints):
|
||||
from sympy.vector.functions import laplacian
|
||||
return laplacian(self._expr)
|
||||
|
||||
|
||||
def _diff_conditional(expr, base_scalar, coeff_1, coeff_2):
|
||||
"""
|
||||
First re-expresses expr in the system that base_scalar belongs to.
|
||||
If base_scalar appears in the re-expressed form, differentiates
|
||||
it wrt base_scalar.
|
||||
Else, returns 0
|
||||
"""
|
||||
from sympy.vector.functions import express
|
||||
new_expr = express(expr, base_scalar.system, variables=True)
|
||||
arg = coeff_1 * coeff_2 * new_expr
|
||||
return Derivative(arg, base_scalar) if arg else S.Zero
|
||||
@@ -0,0 +1,398 @@
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.matrices.dense import (eye, rot_axis1, rot_axis2, rot_axis3)
|
||||
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
||||
from sympy.core.cache import cacheit
|
||||
from sympy.core.symbol import Str
|
||||
import sympy.vector
|
||||
|
||||
|
||||
class Orienter(Basic):
|
||||
"""
|
||||
Super-class for all orienter classes.
|
||||
"""
|
||||
|
||||
def rotation_matrix(self):
|
||||
"""
|
||||
The rotation matrix corresponding to this orienter
|
||||
instance.
|
||||
"""
|
||||
return self._parent_orient
|
||||
|
||||
|
||||
class AxisOrienter(Orienter):
|
||||
"""
|
||||
Class to denote an axis orienter.
|
||||
"""
|
||||
|
||||
def __new__(cls, angle, axis):
|
||||
if not isinstance(axis, sympy.vector.Vector):
|
||||
raise TypeError("axis should be a Vector")
|
||||
angle = sympify(angle)
|
||||
|
||||
obj = super().__new__(cls, angle, axis)
|
||||
obj._angle = angle
|
||||
obj._axis = axis
|
||||
|
||||
return obj
|
||||
|
||||
def __init__(self, angle, axis):
|
||||
"""
|
||||
Axis rotation is a rotation about an arbitrary axis by
|
||||
some angle. The angle is supplied as a SymPy expr scalar, and
|
||||
the axis is supplied as a Vector.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
angle : Expr
|
||||
The angle by which the new system is to be rotated
|
||||
|
||||
axis : Vector
|
||||
The axis around which the rotation has to be performed
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy import symbols
|
||||
>>> q1 = symbols('q1')
|
||||
>>> N = CoordSys3D('N')
|
||||
>>> from sympy.vector import AxisOrienter
|
||||
>>> orienter = AxisOrienter(q1, N.i + 2 * N.j)
|
||||
>>> B = N.orient_new('B', (orienter, ))
|
||||
|
||||
"""
|
||||
# Dummy initializer for docstrings
|
||||
pass
|
||||
|
||||
@cacheit
|
||||
def rotation_matrix(self, system):
|
||||
"""
|
||||
The rotation matrix corresponding to this orienter
|
||||
instance.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
system : CoordSys3D
|
||||
The coordinate system wrt which the rotation matrix
|
||||
is to be computed
|
||||
"""
|
||||
|
||||
axis = sympy.vector.express(self.axis, system).normalize()
|
||||
axis = axis.to_matrix(system)
|
||||
theta = self.angle
|
||||
parent_orient = ((eye(3) - axis * axis.T) * cos(theta) +
|
||||
Matrix([[0, -axis[2], axis[1]],
|
||||
[axis[2], 0, -axis[0]],
|
||||
[-axis[1], axis[0], 0]]) * sin(theta) +
|
||||
axis * axis.T)
|
||||
parent_orient = parent_orient.T
|
||||
return parent_orient
|
||||
|
||||
@property
|
||||
def angle(self):
|
||||
return self._angle
|
||||
|
||||
@property
|
||||
def axis(self):
|
||||
return self._axis
|
||||
|
||||
|
||||
class ThreeAngleOrienter(Orienter):
|
||||
"""
|
||||
Super-class for Body and Space orienters.
|
||||
"""
|
||||
|
||||
def __new__(cls, angle1, angle2, angle3, rot_order):
|
||||
if isinstance(rot_order, Str):
|
||||
rot_order = rot_order.name
|
||||
|
||||
approved_orders = ('123', '231', '312', '132', '213',
|
||||
'321', '121', '131', '212', '232',
|
||||
'313', '323', '')
|
||||
original_rot_order = rot_order
|
||||
rot_order = str(rot_order).upper()
|
||||
if not (len(rot_order) == 3):
|
||||
raise TypeError('rot_order should be a str of length 3')
|
||||
rot_order = [i.replace('X', '1') for i in rot_order]
|
||||
rot_order = [i.replace('Y', '2') for i in rot_order]
|
||||
rot_order = [i.replace('Z', '3') for i in rot_order]
|
||||
rot_order = ''.join(rot_order)
|
||||
if rot_order not in approved_orders:
|
||||
raise TypeError('Invalid rot_type parameter')
|
||||
a1 = int(rot_order[0])
|
||||
a2 = int(rot_order[1])
|
||||
a3 = int(rot_order[2])
|
||||
angle1 = sympify(angle1)
|
||||
angle2 = sympify(angle2)
|
||||
angle3 = sympify(angle3)
|
||||
if cls._in_order:
|
||||
parent_orient = (_rot(a1, angle1) *
|
||||
_rot(a2, angle2) *
|
||||
_rot(a3, angle3))
|
||||
else:
|
||||
parent_orient = (_rot(a3, angle3) *
|
||||
_rot(a2, angle2) *
|
||||
_rot(a1, angle1))
|
||||
parent_orient = parent_orient.T
|
||||
|
||||
obj = super().__new__(
|
||||
cls, angle1, angle2, angle3, Str(rot_order))
|
||||
obj._angle1 = angle1
|
||||
obj._angle2 = angle2
|
||||
obj._angle3 = angle3
|
||||
obj._rot_order = original_rot_order
|
||||
obj._parent_orient = parent_orient
|
||||
|
||||
return obj
|
||||
|
||||
@property
|
||||
def angle1(self):
|
||||
return self._angle1
|
||||
|
||||
@property
|
||||
def angle2(self):
|
||||
return self._angle2
|
||||
|
||||
@property
|
||||
def angle3(self):
|
||||
return self._angle3
|
||||
|
||||
@property
|
||||
def rot_order(self):
|
||||
return self._rot_order
|
||||
|
||||
|
||||
class BodyOrienter(ThreeAngleOrienter):
|
||||
"""
|
||||
Class to denote a body-orienter.
|
||||
"""
|
||||
|
||||
_in_order = True
|
||||
|
||||
def __new__(cls, angle1, angle2, angle3, rot_order):
|
||||
obj = ThreeAngleOrienter.__new__(cls, angle1, angle2, angle3,
|
||||
rot_order)
|
||||
return obj
|
||||
|
||||
def __init__(self, angle1, angle2, angle3, rot_order):
|
||||
"""
|
||||
Body orientation takes this coordinate system through three
|
||||
successive simple rotations.
|
||||
|
||||
Body fixed rotations include both Euler Angles and
|
||||
Tait-Bryan Angles, see https://en.wikipedia.org/wiki/Euler_angles.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
angle1, angle2, angle3 : Expr
|
||||
Three successive angles to rotate the coordinate system by
|
||||
|
||||
rotation_order : string
|
||||
String defining the order of axes for rotation
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, BodyOrienter
|
||||
>>> from sympy import symbols
|
||||
>>> q1, q2, q3 = symbols('q1 q2 q3')
|
||||
>>> N = CoordSys3D('N')
|
||||
|
||||
A 'Body' fixed rotation is described by three angles and
|
||||
three body-fixed rotation axes. To orient a coordinate system D
|
||||
with respect to N, each sequential rotation is always about
|
||||
the orthogonal unit vectors fixed to D. For example, a '123'
|
||||
rotation will specify rotations about N.i, then D.j, then
|
||||
D.k. (Initially, D.i is same as N.i)
|
||||
Therefore,
|
||||
|
||||
>>> body_orienter = BodyOrienter(q1, q2, q3, '123')
|
||||
>>> D = N.orient_new('D', (body_orienter, ))
|
||||
|
||||
is same as
|
||||
|
||||
>>> from sympy.vector import AxisOrienter
|
||||
>>> axis_orienter1 = AxisOrienter(q1, N.i)
|
||||
>>> D = N.orient_new('D', (axis_orienter1, ))
|
||||
>>> axis_orienter2 = AxisOrienter(q2, D.j)
|
||||
>>> D = D.orient_new('D', (axis_orienter2, ))
|
||||
>>> axis_orienter3 = AxisOrienter(q3, D.k)
|
||||
>>> D = D.orient_new('D', (axis_orienter3, ))
|
||||
|
||||
Acceptable rotation orders are of length 3, expressed in XYZ or
|
||||
123, and cannot have a rotation about about an axis twice in a row.
|
||||
|
||||
>>> body_orienter1 = BodyOrienter(q1, q2, q3, '123')
|
||||
>>> body_orienter2 = BodyOrienter(q1, q2, 0, 'ZXZ')
|
||||
>>> body_orienter3 = BodyOrienter(0, 0, 0, 'XYX')
|
||||
|
||||
"""
|
||||
# Dummy initializer for docstrings
|
||||
pass
|
||||
|
||||
|
||||
class SpaceOrienter(ThreeAngleOrienter):
|
||||
"""
|
||||
Class to denote a space-orienter.
|
||||
"""
|
||||
|
||||
_in_order = False
|
||||
|
||||
def __new__(cls, angle1, angle2, angle3, rot_order):
|
||||
obj = ThreeAngleOrienter.__new__(cls, angle1, angle2, angle3,
|
||||
rot_order)
|
||||
return obj
|
||||
|
||||
def __init__(self, angle1, angle2, angle3, rot_order):
|
||||
"""
|
||||
Space rotation is similar to Body rotation, but the rotations
|
||||
are applied in the opposite order.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
angle1, angle2, angle3 : Expr
|
||||
Three successive angles to rotate the coordinate system by
|
||||
|
||||
rotation_order : string
|
||||
String defining the order of axes for rotation
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
BodyOrienter : Orienter to orient systems wrt Euler angles.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, SpaceOrienter
|
||||
>>> from sympy import symbols
|
||||
>>> q1, q2, q3 = symbols('q1 q2 q3')
|
||||
>>> N = CoordSys3D('N')
|
||||
|
||||
To orient a coordinate system D with respect to N, each
|
||||
sequential rotation is always about N's orthogonal unit vectors.
|
||||
For example, a '123' rotation will specify rotations about
|
||||
N.i, then N.j, then N.k.
|
||||
Therefore,
|
||||
|
||||
>>> space_orienter = SpaceOrienter(q1, q2, q3, '312')
|
||||
>>> D = N.orient_new('D', (space_orienter, ))
|
||||
|
||||
is same as
|
||||
|
||||
>>> from sympy.vector import AxisOrienter
|
||||
>>> axis_orienter1 = AxisOrienter(q1, N.i)
|
||||
>>> B = N.orient_new('B', (axis_orienter1, ))
|
||||
>>> axis_orienter2 = AxisOrienter(q2, N.j)
|
||||
>>> C = B.orient_new('C', (axis_orienter2, ))
|
||||
>>> axis_orienter3 = AxisOrienter(q3, N.k)
|
||||
>>> D = C.orient_new('C', (axis_orienter3, ))
|
||||
|
||||
"""
|
||||
# Dummy initializer for docstrings
|
||||
pass
|
||||
|
||||
|
||||
class QuaternionOrienter(Orienter):
|
||||
"""
|
||||
Class to denote a quaternion-orienter.
|
||||
"""
|
||||
|
||||
def __new__(cls, q0, q1, q2, q3):
|
||||
q0 = sympify(q0)
|
||||
q1 = sympify(q1)
|
||||
q2 = sympify(q2)
|
||||
q3 = sympify(q3)
|
||||
parent_orient = (Matrix([[q0 ** 2 + q1 ** 2 - q2 ** 2 -
|
||||
q3 ** 2,
|
||||
2 * (q1 * q2 - q0 * q3),
|
||||
2 * (q0 * q2 + q1 * q3)],
|
||||
[2 * (q1 * q2 + q0 * q3),
|
||||
q0 ** 2 - q1 ** 2 +
|
||||
q2 ** 2 - q3 ** 2,
|
||||
2 * (q2 * q3 - q0 * q1)],
|
||||
[2 * (q1 * q3 - q0 * q2),
|
||||
2 * (q0 * q1 + q2 * q3),
|
||||
q0 ** 2 - q1 ** 2 -
|
||||
q2 ** 2 + q3 ** 2]]))
|
||||
parent_orient = parent_orient.T
|
||||
|
||||
obj = super().__new__(cls, q0, q1, q2, q3)
|
||||
obj._q0 = q0
|
||||
obj._q1 = q1
|
||||
obj._q2 = q2
|
||||
obj._q3 = q3
|
||||
obj._parent_orient = parent_orient
|
||||
|
||||
return obj
|
||||
|
||||
def __init__(self, angle1, angle2, angle3, rot_order):
|
||||
"""
|
||||
Quaternion orientation orients the new CoordSys3D with
|
||||
Quaternions, defined as a finite rotation about lambda, a unit
|
||||
vector, by some amount theta.
|
||||
|
||||
This orientation is described by four parameters:
|
||||
|
||||
q0 = cos(theta/2)
|
||||
|
||||
q1 = lambda_x sin(theta/2)
|
||||
|
||||
q2 = lambda_y sin(theta/2)
|
||||
|
||||
q3 = lambda_z sin(theta/2)
|
||||
|
||||
Quaternion does not take in a rotation order.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
q0, q1, q2, q3 : Expr
|
||||
The quaternions to rotate the coordinate system by
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy import symbols
|
||||
>>> q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
|
||||
>>> N = CoordSys3D('N')
|
||||
>>> from sympy.vector import QuaternionOrienter
|
||||
>>> q_orienter = QuaternionOrienter(q0, q1, q2, q3)
|
||||
>>> B = N.orient_new('B', (q_orienter, ))
|
||||
|
||||
"""
|
||||
# Dummy initializer for docstrings
|
||||
pass
|
||||
|
||||
@property
|
||||
def q0(self):
|
||||
return self._q0
|
||||
|
||||
@property
|
||||
def q1(self):
|
||||
return self._q1
|
||||
|
||||
@property
|
||||
def q2(self):
|
||||
return self._q2
|
||||
|
||||
@property
|
||||
def q3(self):
|
||||
return self._q3
|
||||
|
||||
|
||||
def _rot(axis, angle):
|
||||
"""DCM for simple axis 1, 2 or 3 rotations. """
|
||||
if axis == 1:
|
||||
return Matrix(rot_axis1(angle).T)
|
||||
elif axis == 2:
|
||||
return Matrix(rot_axis2(angle).T)
|
||||
elif axis == 3:
|
||||
return Matrix(rot_axis3(angle).T)
|
||||
@@ -0,0 +1,189 @@
|
||||
from functools import singledispatch
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.functions.elementary.trigonometric import tan
|
||||
from sympy.simplify import trigsimp
|
||||
from sympy.core import Basic, Tuple
|
||||
from sympy.core.symbol import _symbol
|
||||
from sympy.solvers import solve
|
||||
from sympy.geometry import Point, Segment, Curve, Ellipse, Polygon
|
||||
from sympy.vector import ImplicitRegion
|
||||
|
||||
|
||||
class ParametricRegion(Basic):
|
||||
"""
|
||||
Represents a parametric region in space.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import cos, sin, pi
|
||||
>>> from sympy.abc import r, theta, t, a, b, x, y
|
||||
>>> from sympy.vector import ParametricRegion
|
||||
|
||||
>>> ParametricRegion((t, t**2), (t, -1, 2))
|
||||
ParametricRegion((t, t**2), (t, -1, 2))
|
||||
>>> ParametricRegion((x, y), (x, 3, 4), (y, 5, 6))
|
||||
ParametricRegion((x, y), (x, 3, 4), (y, 5, 6))
|
||||
>>> ParametricRegion((r*cos(theta), r*sin(theta)), (r, -2, 2), (theta, 0, pi))
|
||||
ParametricRegion((r*cos(theta), r*sin(theta)), (r, -2, 2), (theta, 0, pi))
|
||||
>>> ParametricRegion((a*cos(t), b*sin(t)), t)
|
||||
ParametricRegion((a*cos(t), b*sin(t)), t)
|
||||
|
||||
>>> circle = ParametricRegion((r*cos(theta), r*sin(theta)), r, (theta, 0, pi))
|
||||
>>> circle.parameters
|
||||
(r, theta)
|
||||
>>> circle.definition
|
||||
(r*cos(theta), r*sin(theta))
|
||||
>>> circle.limits
|
||||
{theta: (0, pi)}
|
||||
|
||||
Dimension of a parametric region determines whether a region is a curve, surface
|
||||
or volume region. It does not represent its dimensions in space.
|
||||
|
||||
>>> circle.dimensions
|
||||
1
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
definition : tuple to define base scalars in terms of parameters.
|
||||
|
||||
bounds : Parameter or a tuple of length 3 to define parameter and corresponding lower and upper bound.
|
||||
|
||||
"""
|
||||
def __new__(cls, definition, *bounds):
|
||||
parameters = ()
|
||||
limits = {}
|
||||
|
||||
if not isinstance(bounds, Tuple):
|
||||
bounds = Tuple(*bounds)
|
||||
|
||||
for bound in bounds:
|
||||
if isinstance(bound, (tuple, Tuple)):
|
||||
if len(bound) != 3:
|
||||
raise ValueError("Tuple should be in the form (parameter, lowerbound, upperbound)")
|
||||
parameters += (bound[0],)
|
||||
limits[bound[0]] = (bound[1], bound[2])
|
||||
else:
|
||||
parameters += (bound,)
|
||||
|
||||
if not isinstance(definition, (tuple, Tuple)):
|
||||
definition = (definition,)
|
||||
|
||||
obj = super().__new__(cls, Tuple(*definition), *bounds)
|
||||
obj._parameters = parameters
|
||||
obj._limits = limits
|
||||
|
||||
return obj
|
||||
|
||||
@property
|
||||
def definition(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def limits(self):
|
||||
return self._limits
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
return self._parameters
|
||||
|
||||
@property
|
||||
def dimensions(self):
|
||||
return len(self.limits)
|
||||
|
||||
|
||||
@singledispatch
|
||||
def parametric_region_list(reg):
|
||||
"""
|
||||
Returns a list of ParametricRegion objects representing the geometric region.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import t
|
||||
>>> from sympy.vector import parametric_region_list
|
||||
>>> from sympy.geometry import Point, Curve, Ellipse, Segment, Polygon
|
||||
|
||||
>>> p = Point(2, 5)
|
||||
>>> parametric_region_list(p)
|
||||
[ParametricRegion((2, 5))]
|
||||
|
||||
>>> c = Curve((t**3, 4*t), (t, -3, 4))
|
||||
>>> parametric_region_list(c)
|
||||
[ParametricRegion((t**3, 4*t), (t, -3, 4))]
|
||||
|
||||
>>> e = Ellipse(Point(1, 3), 2, 3)
|
||||
>>> parametric_region_list(e)
|
||||
[ParametricRegion((2*cos(t) + 1, 3*sin(t) + 3), (t, 0, 2*pi))]
|
||||
|
||||
>>> s = Segment(Point(1, 3), Point(2, 6))
|
||||
>>> parametric_region_list(s)
|
||||
[ParametricRegion((t + 1, 3*t + 3), (t, 0, 1))]
|
||||
|
||||
>>> p1, p2, p3, p4 = [(0, 1), (2, -3), (5, 3), (-2, 3)]
|
||||
>>> poly = Polygon(p1, p2, p3, p4)
|
||||
>>> parametric_region_list(poly)
|
||||
[ParametricRegion((2*t, 1 - 4*t), (t, 0, 1)), ParametricRegion((3*t + 2, 6*t - 3), (t, 0, 1)),\
|
||||
ParametricRegion((5 - 7*t, 3), (t, 0, 1)), ParametricRegion((2*t - 2, 3 - 2*t), (t, 0, 1))]
|
||||
|
||||
"""
|
||||
raise ValueError("SymPy cannot determine parametric representation of the region.")
|
||||
|
||||
|
||||
@parametric_region_list.register(Point)
|
||||
def _(obj):
|
||||
return [ParametricRegion(obj.args)]
|
||||
|
||||
|
||||
@parametric_region_list.register(Curve) # type: ignore
|
||||
def _(obj):
|
||||
definition = obj.arbitrary_point(obj.parameter).args
|
||||
bounds = obj.limits
|
||||
return [ParametricRegion(definition, bounds)]
|
||||
|
||||
|
||||
@parametric_region_list.register(Ellipse) # type: ignore
|
||||
def _(obj, parameter='t'):
|
||||
definition = obj.arbitrary_point(parameter).args
|
||||
t = _symbol(parameter, real=True)
|
||||
bounds = (t, 0, 2*pi)
|
||||
return [ParametricRegion(definition, bounds)]
|
||||
|
||||
|
||||
@parametric_region_list.register(Segment) # type: ignore
|
||||
def _(obj, parameter='t'):
|
||||
t = _symbol(parameter, real=True)
|
||||
definition = obj.arbitrary_point(t).args
|
||||
|
||||
for i in range(0, 3):
|
||||
lower_bound = solve(definition[i] - obj.points[0].args[i], t)
|
||||
upper_bound = solve(definition[i] - obj.points[1].args[i], t)
|
||||
|
||||
if len(lower_bound) == 1 and len(upper_bound) == 1:
|
||||
bounds = t, lower_bound[0], upper_bound[0]
|
||||
break
|
||||
|
||||
definition_tuple = obj.arbitrary_point(parameter).args
|
||||
return [ParametricRegion(definition_tuple, bounds)]
|
||||
|
||||
|
||||
@parametric_region_list.register(Polygon) # type: ignore
|
||||
def _(obj, parameter='t'):
|
||||
l = [parametric_region_list(side, parameter)[0] for side in obj.sides]
|
||||
return l
|
||||
|
||||
|
||||
@parametric_region_list.register(ImplicitRegion) # type: ignore
|
||||
def _(obj, parameters=('t', 's')):
|
||||
definition = obj.rational_parametrization(parameters)
|
||||
bounds = []
|
||||
|
||||
for i in range(len(obj.variables) - 1):
|
||||
# Each parameter is replaced by its tangent to simplify integration
|
||||
parameter = _symbol(parameters[i], real=True)
|
||||
definition = [trigsimp(elem.subs(parameter, tan(parameter/2))) for elem in definition]
|
||||
bounds.append((parameter, 0, 2*pi),)
|
||||
|
||||
definition = Tuple(*definition)
|
||||
return [ParametricRegion(definition, *bounds)]
|
||||
@@ -0,0 +1,148 @@
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.symbol import Str
|
||||
from sympy.vector.vector import Vector
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.functions import _path
|
||||
from sympy.core.cache import cacheit
|
||||
|
||||
|
||||
class Point(Basic):
|
||||
"""
|
||||
Represents a point in 3-D space.
|
||||
"""
|
||||
|
||||
def __new__(cls, name, position=Vector.zero, parent_point=None):
|
||||
name = str(name)
|
||||
# Check the args first
|
||||
if not isinstance(position, Vector):
|
||||
raise TypeError(
|
||||
"position should be an instance of Vector, not %s" % type(
|
||||
position))
|
||||
if (not isinstance(parent_point, Point) and
|
||||
parent_point is not None):
|
||||
raise TypeError(
|
||||
"parent_point should be an instance of Point, not %s" % type(
|
||||
parent_point))
|
||||
# Super class construction
|
||||
if parent_point is None:
|
||||
obj = super().__new__(cls, Str(name), position)
|
||||
else:
|
||||
obj = super().__new__(cls, Str(name), position, parent_point)
|
||||
# Decide the object parameters
|
||||
obj._name = name
|
||||
obj._pos = position
|
||||
if parent_point is None:
|
||||
obj._parent = None
|
||||
obj._root = obj
|
||||
else:
|
||||
obj._parent = parent_point
|
||||
obj._root = parent_point._root
|
||||
# Return object
|
||||
return obj
|
||||
|
||||
@cacheit
|
||||
def position_wrt(self, other):
|
||||
"""
|
||||
Returns the position vector of this Point with respect to
|
||||
another Point/CoordSys3D.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other : Point/CoordSys3D
|
||||
If other is a Point, the position of this Point wrt it is
|
||||
returned. If its an instance of CoordSyRect, the position
|
||||
wrt its origin is returned.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> N = CoordSys3D('N')
|
||||
>>> p1 = N.origin.locate_new('p1', 10 * N.i)
|
||||
>>> N.origin.position_wrt(p1)
|
||||
(-10)*N.i
|
||||
|
||||
"""
|
||||
|
||||
if (not isinstance(other, Point) and
|
||||
not isinstance(other, CoordSys3D)):
|
||||
raise TypeError(str(other) +
|
||||
"is not a Point or CoordSys3D")
|
||||
if isinstance(other, CoordSys3D):
|
||||
other = other.origin
|
||||
# Handle special cases
|
||||
if other == self:
|
||||
return Vector.zero
|
||||
elif other == self._parent:
|
||||
return self._pos
|
||||
elif other._parent == self:
|
||||
return -1 * other._pos
|
||||
# Else, use point tree to calculate position
|
||||
rootindex, path = _path(self, other)
|
||||
result = Vector.zero
|
||||
for i in range(rootindex):
|
||||
result += path[i]._pos
|
||||
for i in range(rootindex + 1, len(path)):
|
||||
result -= path[i]._pos
|
||||
return result
|
||||
|
||||
def locate_new(self, name, position):
|
||||
"""
|
||||
Returns a new Point located at the given position wrt this
|
||||
Point.
|
||||
Thus, the position vector of the new Point wrt this one will
|
||||
be equal to the given 'position' parameter.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
name : str
|
||||
Name of the new point
|
||||
|
||||
position : Vector
|
||||
The position vector of the new Point wrt this one
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> N = CoordSys3D('N')
|
||||
>>> p1 = N.origin.locate_new('p1', 10 * N.i)
|
||||
>>> p1.position_wrt(N.origin)
|
||||
10*N.i
|
||||
|
||||
"""
|
||||
return Point(name, position, self)
|
||||
|
||||
def express_coordinates(self, coordinate_system):
|
||||
"""
|
||||
Returns the Cartesian/rectangular coordinates of this point
|
||||
wrt the origin of the given CoordSys3D instance.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
coordinate_system : CoordSys3D
|
||||
The coordinate system to express the coordinates of this
|
||||
Point in.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> N = CoordSys3D('N')
|
||||
>>> p1 = N.origin.locate_new('p1', 10 * N.i)
|
||||
>>> p2 = p1.locate_new('p2', 5 * N.j)
|
||||
>>> p2.express_coordinates(N)
|
||||
(10, 5, 0)
|
||||
|
||||
"""
|
||||
|
||||
# Determine the position vector
|
||||
pos_vect = self.position_wrt(coordinate_system.origin)
|
||||
# Express it in the given coordinate system
|
||||
return tuple(pos_vect.to_matrix(coordinate_system))
|
||||
|
||||
def _sympystr(self, printer):
|
||||
return self._name
|
||||
@@ -0,0 +1,72 @@
|
||||
from sympy.core import AtomicExpr, Symbol, S
|
||||
from sympy.core.sympify import _sympify
|
||||
from sympy.printing.pretty.stringpict import prettyForm
|
||||
from sympy.printing.precedence import PRECEDENCE
|
||||
from sympy.core.kind import NumberKind
|
||||
|
||||
|
||||
class BaseScalar(AtomicExpr):
|
||||
"""
|
||||
A coordinate symbol/base scalar.
|
||||
|
||||
Ideally, users should not instantiate this class.
|
||||
|
||||
"""
|
||||
|
||||
kind = NumberKind
|
||||
|
||||
def __new__(cls, index, system, pretty_str=None, latex_str=None):
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
if pretty_str is None:
|
||||
pretty_str = "x{}".format(index)
|
||||
elif isinstance(pretty_str, Symbol):
|
||||
pretty_str = pretty_str.name
|
||||
if latex_str is None:
|
||||
latex_str = "x_{}".format(index)
|
||||
elif isinstance(latex_str, Symbol):
|
||||
latex_str = latex_str.name
|
||||
|
||||
index = _sympify(index)
|
||||
system = _sympify(system)
|
||||
obj = super().__new__(cls, index, system)
|
||||
if not isinstance(system, CoordSys3D):
|
||||
raise TypeError("system should be a CoordSys3D")
|
||||
if index not in range(0, 3):
|
||||
raise ValueError("Invalid index specified.")
|
||||
# The _id is used for equating purposes, and for hashing
|
||||
obj._id = (index, system)
|
||||
obj._name = obj.name = system._name + '.' + system._variable_names[index]
|
||||
obj._pretty_form = '' + pretty_str
|
||||
obj._latex_form = latex_str
|
||||
obj._system = system
|
||||
|
||||
return obj
|
||||
|
||||
is_commutative = True
|
||||
is_symbol = True
|
||||
|
||||
@property
|
||||
def free_symbols(self):
|
||||
return {self}
|
||||
|
||||
_diff_wrt = True
|
||||
|
||||
def _eval_derivative(self, s):
|
||||
if self == s:
|
||||
return S.One
|
||||
return S.Zero
|
||||
|
||||
def _latex(self, printer=None):
|
||||
return self._latex_form
|
||||
|
||||
def _pretty(self, printer=None):
|
||||
return prettyForm(self._pretty_form)
|
||||
|
||||
precedence = PRECEDENCE['Atom']
|
||||
|
||||
@property
|
||||
def system(self):
|
||||
return self._system
|
||||
|
||||
def _sympystr(self, printer):
|
||||
return self._name
|
||||
@@ -0,0 +1,464 @@
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.scalar import BaseScalar
|
||||
from sympy.core.function import expand
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.hyperbolic import (cosh, sinh)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (acos, atan2, cos, sin)
|
||||
from sympy.matrices.dense import zeros
|
||||
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.vector.functions import express
|
||||
from sympy.vector.point import Point
|
||||
from sympy.vector.vector import Vector
|
||||
from sympy.vector.orienters import (AxisOrienter, BodyOrienter,
|
||||
SpaceOrienter, QuaternionOrienter)
|
||||
|
||||
|
||||
x, y, z = symbols('x y z')
|
||||
a, b, c, q = symbols('a b c q')
|
||||
q1, q2, q3, q4 = symbols('q1 q2 q3 q4')
|
||||
|
||||
|
||||
def test_func_args():
|
||||
A = CoordSys3D('A')
|
||||
assert A.x.func(*A.x.args) == A.x
|
||||
expr = 3*A.x + 4*A.y
|
||||
assert expr.func(*expr.args) == expr
|
||||
assert A.i.func(*A.i.args) == A.i
|
||||
v = A.x*A.i + A.y*A.j + A.z*A.k
|
||||
assert v.func(*v.args) == v
|
||||
assert A.origin.func(*A.origin.args) == A.origin
|
||||
|
||||
|
||||
def test_coordsys3d_equivalence():
|
||||
A = CoordSys3D('A')
|
||||
A1 = CoordSys3D('A')
|
||||
assert A1 == A
|
||||
B = CoordSys3D('B')
|
||||
assert A != B
|
||||
|
||||
|
||||
def test_orienters():
|
||||
A = CoordSys3D('A')
|
||||
axis_orienter = AxisOrienter(a, A.k)
|
||||
body_orienter = BodyOrienter(a, b, c, '123')
|
||||
space_orienter = SpaceOrienter(a, b, c, '123')
|
||||
q_orienter = QuaternionOrienter(q1, q2, q3, q4)
|
||||
assert axis_orienter.rotation_matrix(A) == Matrix([
|
||||
[ cos(a), sin(a), 0],
|
||||
[-sin(a), cos(a), 0],
|
||||
[ 0, 0, 1]])
|
||||
assert body_orienter.rotation_matrix() == Matrix([
|
||||
[ cos(b)*cos(c), sin(a)*sin(b)*cos(c) + sin(c)*cos(a),
|
||||
sin(a)*sin(c) - sin(b)*cos(a)*cos(c)],
|
||||
[-sin(c)*cos(b), -sin(a)*sin(b)*sin(c) + cos(a)*cos(c),
|
||||
sin(a)*cos(c) + sin(b)*sin(c)*cos(a)],
|
||||
[ sin(b), -sin(a)*cos(b),
|
||||
cos(a)*cos(b)]])
|
||||
assert space_orienter.rotation_matrix() == Matrix([
|
||||
[cos(b)*cos(c), sin(c)*cos(b), -sin(b)],
|
||||
[sin(a)*sin(b)*cos(c) - sin(c)*cos(a),
|
||||
sin(a)*sin(b)*sin(c) + cos(a)*cos(c), sin(a)*cos(b)],
|
||||
[sin(a)*sin(c) + sin(b)*cos(a)*cos(c), -sin(a)*cos(c) +
|
||||
sin(b)*sin(c)*cos(a), cos(a)*cos(b)]])
|
||||
assert q_orienter.rotation_matrix() == Matrix([
|
||||
[q1**2 + q2**2 - q3**2 - q4**2, 2*q1*q4 + 2*q2*q3,
|
||||
-2*q1*q3 + 2*q2*q4],
|
||||
[-2*q1*q4 + 2*q2*q3, q1**2 - q2**2 + q3**2 - q4**2,
|
||||
2*q1*q2 + 2*q3*q4],
|
||||
[2*q1*q3 + 2*q2*q4,
|
||||
-2*q1*q2 + 2*q3*q4, q1**2 - q2**2 - q3**2 + q4**2]])
|
||||
|
||||
|
||||
def test_coordinate_vars():
|
||||
"""
|
||||
Tests the coordinate variables functionality with respect to
|
||||
reorientation of coordinate systems.
|
||||
"""
|
||||
A = CoordSys3D('A')
|
||||
# Note that the name given on the lhs is different from A.x._name
|
||||
assert BaseScalar(0, A, 'A_x', r'\mathbf{{x}_{A}}') == A.x
|
||||
assert BaseScalar(1, A, 'A_y', r'\mathbf{{y}_{A}}') == A.y
|
||||
assert BaseScalar(2, A, 'A_z', r'\mathbf{{z}_{A}}') == A.z
|
||||
assert BaseScalar(0, A, 'A_x', r'\mathbf{{x}_{A}}').__hash__() == A.x.__hash__()
|
||||
assert isinstance(A.x, BaseScalar) and \
|
||||
isinstance(A.y, BaseScalar) and \
|
||||
isinstance(A.z, BaseScalar)
|
||||
assert A.x*A.y == A.y*A.x
|
||||
assert A.scalar_map(A) == {A.x: A.x, A.y: A.y, A.z: A.z}
|
||||
assert A.x.system == A
|
||||
assert A.x.diff(A.x) == 1
|
||||
B = A.orient_new_axis('B', q, A.k)
|
||||
assert B.scalar_map(A) == {B.z: A.z, B.y: -A.x*sin(q) + A.y*cos(q),
|
||||
B.x: A.x*cos(q) + A.y*sin(q)}
|
||||
assert A.scalar_map(B) == {A.x: B.x*cos(q) - B.y*sin(q),
|
||||
A.y: B.x*sin(q) + B.y*cos(q), A.z: B.z}
|
||||
assert express(B.x, A, variables=True) == A.x*cos(q) + A.y*sin(q)
|
||||
assert express(B.y, A, variables=True) == -A.x*sin(q) + A.y*cos(q)
|
||||
assert express(B.z, A, variables=True) == A.z
|
||||
assert expand(express(B.x*B.y*B.z, A, variables=True)) == \
|
||||
expand(A.z*(-A.x*sin(q) + A.y*cos(q))*(A.x*cos(q) + A.y*sin(q)))
|
||||
assert express(B.x*B.i + B.y*B.j + B.z*B.k, A) == \
|
||||
(B.x*cos(q) - B.y*sin(q))*A.i + (B.x*sin(q) + \
|
||||
B.y*cos(q))*A.j + B.z*A.k
|
||||
assert simplify(express(B.x*B.i + B.y*B.j + B.z*B.k, A, \
|
||||
variables=True)) == \
|
||||
A.x*A.i + A.y*A.j + A.z*A.k
|
||||
assert express(A.x*A.i + A.y*A.j + A.z*A.k, B) == \
|
||||
(A.x*cos(q) + A.y*sin(q))*B.i + \
|
||||
(-A.x*sin(q) + A.y*cos(q))*B.j + A.z*B.k
|
||||
assert simplify(express(A.x*A.i + A.y*A.j + A.z*A.k, B, \
|
||||
variables=True)) == \
|
||||
B.x*B.i + B.y*B.j + B.z*B.k
|
||||
N = B.orient_new_axis('N', -q, B.k)
|
||||
assert N.scalar_map(A) == \
|
||||
{N.x: A.x, N.z: A.z, N.y: A.y}
|
||||
C = A.orient_new_axis('C', q, A.i + A.j + A.k)
|
||||
mapping = A.scalar_map(C)
|
||||
assert mapping[A.x].equals(C.x*(2*cos(q) + 1)/3 +
|
||||
C.y*(-2*sin(q + pi/6) + 1)/3 +
|
||||
C.z*(-2*cos(q + pi/3) + 1)/3)
|
||||
assert mapping[A.y].equals(C.x*(-2*cos(q + pi/3) + 1)/3 +
|
||||
C.y*(2*cos(q) + 1)/3 +
|
||||
C.z*(-2*sin(q + pi/6) + 1)/3)
|
||||
assert mapping[A.z].equals(C.x*(-2*sin(q + pi/6) + 1)/3 +
|
||||
C.y*(-2*cos(q + pi/3) + 1)/3 +
|
||||
C.z*(2*cos(q) + 1)/3)
|
||||
D = A.locate_new('D', a*A.i + b*A.j + c*A.k)
|
||||
assert D.scalar_map(A) == {D.z: A.z - c, D.x: A.x - a, D.y: A.y - b}
|
||||
E = A.orient_new_axis('E', a, A.k, a*A.i + b*A.j + c*A.k)
|
||||
assert A.scalar_map(E) == {A.z: E.z + c,
|
||||
A.x: E.x*cos(a) - E.y*sin(a) + a,
|
||||
A.y: E.x*sin(a) + E.y*cos(a) + b}
|
||||
assert E.scalar_map(A) == {E.x: (A.x - a)*cos(a) + (A.y - b)*sin(a),
|
||||
E.y: (-A.x + a)*sin(a) + (A.y - b)*cos(a),
|
||||
E.z: A.z - c}
|
||||
F = A.locate_new('F', Vector.zero)
|
||||
assert A.scalar_map(F) == {A.z: F.z, A.x: F.x, A.y: F.y}
|
||||
|
||||
|
||||
def test_rotation_matrix():
|
||||
N = CoordSys3D('N')
|
||||
A = N.orient_new_axis('A', q1, N.k)
|
||||
B = A.orient_new_axis('B', q2, A.i)
|
||||
C = B.orient_new_axis('C', q3, B.j)
|
||||
D = N.orient_new_axis('D', q4, N.j)
|
||||
E = N.orient_new_space('E', q1, q2, q3, '123')
|
||||
F = N.orient_new_quaternion('F', q1, q2, q3, q4)
|
||||
G = N.orient_new_body('G', q1, q2, q3, '123')
|
||||
assert N.rotation_matrix(C) == Matrix([
|
||||
[- sin(q1) * sin(q2) * sin(q3) + cos(q1) * cos(q3), - sin(q1) *
|
||||
cos(q2), sin(q1) * sin(q2) * cos(q3) + sin(q3) * cos(q1)], \
|
||||
[sin(q1) * cos(q3) + sin(q2) * sin(q3) * cos(q1), \
|
||||
cos(q1) * cos(q2), sin(q1) * sin(q3) - sin(q2) * cos(q1) * \
|
||||
cos(q3)], [- sin(q3) * cos(q2), sin(q2), cos(q2) * cos(q3)]])
|
||||
test_mat = D.rotation_matrix(C) - Matrix(
|
||||
[[cos(q1) * cos(q3) * cos(q4) - sin(q3) * (- sin(q4) * cos(q2) +
|
||||
sin(q1) * sin(q2) * cos(q4)), - sin(q2) * sin(q4) - sin(q1) *
|
||||
cos(q2) * cos(q4), sin(q3) * cos(q1) * cos(q4) + cos(q3) * \
|
||||
(- sin(q4) * cos(q2) + sin(q1) * sin(q2) * cos(q4))], \
|
||||
[sin(q1) * cos(q3) + sin(q2) * sin(q3) * cos(q1), cos(q1) * \
|
||||
cos(q2), sin(q1) * sin(q3) - sin(q2) * cos(q1) * cos(q3)], \
|
||||
[sin(q4) * cos(q1) * cos(q3) - sin(q3) * (cos(q2) * cos(q4) + \
|
||||
sin(q1) * sin(q2) * \
|
||||
sin(q4)), sin(q2) *
|
||||
cos(q4) - sin(q1) * sin(q4) * cos(q2), sin(q3) * \
|
||||
sin(q4) * cos(q1) + cos(q3) * (cos(q2) * cos(q4) + \
|
||||
sin(q1) * sin(q2) * sin(q4))]])
|
||||
assert test_mat.expand() == zeros(3, 3)
|
||||
assert E.rotation_matrix(N) == Matrix(
|
||||
[[cos(q2)*cos(q3), sin(q3)*cos(q2), -sin(q2)],
|
||||
[sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), \
|
||||
sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), sin(q1)*cos(q2)], \
|
||||
[sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3), - \
|
||||
sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2)]])
|
||||
assert F.rotation_matrix(N) == Matrix([[
|
||||
q1**2 + q2**2 - q3**2 - q4**2,
|
||||
2*q1*q4 + 2*q2*q3, -2*q1*q3 + 2*q2*q4],[ -2*q1*q4 + 2*q2*q3,
|
||||
q1**2 - q2**2 + q3**2 - q4**2, 2*q1*q2 + 2*q3*q4],
|
||||
[2*q1*q3 + 2*q2*q4,
|
||||
-2*q1*q2 + 2*q3*q4,
|
||||
q1**2 - q2**2 - q3**2 + q4**2]])
|
||||
assert G.rotation_matrix(N) == Matrix([[
|
||||
cos(q2)*cos(q3), sin(q1)*sin(q2)*cos(q3) + sin(q3)*cos(q1),
|
||||
sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3)], [
|
||||
-sin(q3)*cos(q2), -sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3),
|
||||
sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)],[
|
||||
sin(q2), -sin(q1)*cos(q2), cos(q1)*cos(q2)]])
|
||||
|
||||
|
||||
def test_vector_with_orientation():
|
||||
"""
|
||||
Tests the effects of orientation of coordinate systems on
|
||||
basic vector operations.
|
||||
"""
|
||||
N = CoordSys3D('N')
|
||||
A = N.orient_new_axis('A', q1, N.k)
|
||||
B = A.orient_new_axis('B', q2, A.i)
|
||||
C = B.orient_new_axis('C', q3, B.j)
|
||||
|
||||
# Test to_matrix
|
||||
v1 = a*N.i + b*N.j + c*N.k
|
||||
assert v1.to_matrix(A) == Matrix([[ a*cos(q1) + b*sin(q1)],
|
||||
[-a*sin(q1) + b*cos(q1)],
|
||||
[ c]])
|
||||
|
||||
# Test dot
|
||||
assert N.i.dot(A.i) == cos(q1)
|
||||
assert N.i.dot(A.j) == -sin(q1)
|
||||
assert N.i.dot(A.k) == 0
|
||||
assert N.j.dot(A.i) == sin(q1)
|
||||
assert N.j.dot(A.j) == cos(q1)
|
||||
assert N.j.dot(A.k) == 0
|
||||
assert N.k.dot(A.i) == 0
|
||||
assert N.k.dot(A.j) == 0
|
||||
assert N.k.dot(A.k) == 1
|
||||
|
||||
assert N.i.dot(A.i + A.j) == -sin(q1) + cos(q1) == \
|
||||
(A.i + A.j).dot(N.i)
|
||||
|
||||
assert A.i.dot(C.i) == cos(q3)
|
||||
assert A.i.dot(C.j) == 0
|
||||
assert A.i.dot(C.k) == sin(q3)
|
||||
assert A.j.dot(C.i) == sin(q2)*sin(q3)
|
||||
assert A.j.dot(C.j) == cos(q2)
|
||||
assert A.j.dot(C.k) == -sin(q2)*cos(q3)
|
||||
assert A.k.dot(C.i) == -cos(q2)*sin(q3)
|
||||
assert A.k.dot(C.j) == sin(q2)
|
||||
assert A.k.dot(C.k) == cos(q2)*cos(q3)
|
||||
|
||||
# Test cross
|
||||
assert N.i.cross(A.i) == sin(q1)*A.k
|
||||
assert N.i.cross(A.j) == cos(q1)*A.k
|
||||
assert N.i.cross(A.k) == -sin(q1)*A.i - cos(q1)*A.j
|
||||
assert N.j.cross(A.i) == -cos(q1)*A.k
|
||||
assert N.j.cross(A.j) == sin(q1)*A.k
|
||||
assert N.j.cross(A.k) == cos(q1)*A.i - sin(q1)*A.j
|
||||
assert N.k.cross(A.i) == A.j
|
||||
assert N.k.cross(A.j) == -A.i
|
||||
assert N.k.cross(A.k) == Vector.zero
|
||||
|
||||
assert N.i.cross(A.i) == sin(q1)*A.k
|
||||
assert N.i.cross(A.j) == cos(q1)*A.k
|
||||
assert N.i.cross(A.i + A.j) == sin(q1)*A.k + cos(q1)*A.k
|
||||
assert (A.i + A.j).cross(N.i) == (-sin(q1) - cos(q1))*N.k
|
||||
|
||||
assert A.i.cross(C.i) == sin(q3)*C.j
|
||||
assert A.i.cross(C.j) == -sin(q3)*C.i + cos(q3)*C.k
|
||||
assert A.i.cross(C.k) == -cos(q3)*C.j
|
||||
assert C.i.cross(A.i) == (-sin(q3)*cos(q2))*A.j + \
|
||||
(-sin(q2)*sin(q3))*A.k
|
||||
assert C.j.cross(A.i) == (sin(q2))*A.j + (-cos(q2))*A.k
|
||||
assert express(C.k.cross(A.i), C).trigsimp() == cos(q3)*C.j
|
||||
|
||||
|
||||
def test_orient_new_methods():
|
||||
N = CoordSys3D('N')
|
||||
orienter1 = AxisOrienter(q4, N.j)
|
||||
orienter2 = SpaceOrienter(q1, q2, q3, '123')
|
||||
orienter3 = QuaternionOrienter(q1, q2, q3, q4)
|
||||
orienter4 = BodyOrienter(q1, q2, q3, '123')
|
||||
D = N.orient_new('D', (orienter1, ))
|
||||
E = N.orient_new('E', (orienter2, ))
|
||||
F = N.orient_new('F', (orienter3, ))
|
||||
G = N.orient_new('G', (orienter4, ))
|
||||
assert D == N.orient_new_axis('D', q4, N.j)
|
||||
assert E == N.orient_new_space('E', q1, q2, q3, '123')
|
||||
assert F == N.orient_new_quaternion('F', q1, q2, q3, q4)
|
||||
assert G == N.orient_new_body('G', q1, q2, q3, '123')
|
||||
|
||||
|
||||
def test_locatenew_point():
|
||||
"""
|
||||
Tests Point class, and locate_new method in CoordSys3D.
|
||||
"""
|
||||
A = CoordSys3D('A')
|
||||
assert isinstance(A.origin, Point)
|
||||
v = a*A.i + b*A.j + c*A.k
|
||||
C = A.locate_new('C', v)
|
||||
assert C.origin.position_wrt(A) == \
|
||||
C.position_wrt(A) == \
|
||||
C.origin.position_wrt(A.origin) == v
|
||||
assert A.origin.position_wrt(C) == \
|
||||
A.position_wrt(C) == \
|
||||
A.origin.position_wrt(C.origin) == -v
|
||||
assert A.origin.express_coordinates(C) == (-a, -b, -c)
|
||||
p = A.origin.locate_new('p', -v)
|
||||
assert p.express_coordinates(A) == (-a, -b, -c)
|
||||
assert p.position_wrt(C.origin) == p.position_wrt(C) == \
|
||||
-2 * v
|
||||
p1 = p.locate_new('p1', 2*v)
|
||||
assert p1.position_wrt(C.origin) == Vector.zero
|
||||
assert p1.express_coordinates(C) == (0, 0, 0)
|
||||
p2 = p.locate_new('p2', A.i)
|
||||
assert p1.position_wrt(p2) == 2*v - A.i
|
||||
assert p2.express_coordinates(C) == (-2*a + 1, -2*b, -2*c)
|
||||
|
||||
|
||||
def test_create_new():
|
||||
a = CoordSys3D('a')
|
||||
c = a.create_new('c', transformation='spherical')
|
||||
assert c._parent == a
|
||||
assert c.transformation_to_parent() == \
|
||||
(c.r*sin(c.theta)*cos(c.phi), c.r*sin(c.theta)*sin(c.phi), c.r*cos(c.theta))
|
||||
assert c.transformation_from_parent() == \
|
||||
(sqrt(a.x**2 + a.y**2 + a.z**2), acos(a.z/sqrt(a.x**2 + a.y**2 + a.z**2)), atan2(a.y, a.x))
|
||||
|
||||
|
||||
def test_evalf():
|
||||
A = CoordSys3D('A')
|
||||
v = 3*A.i + 4*A.j + a*A.k
|
||||
assert v.n() == v.evalf()
|
||||
assert v.evalf(subs={a:1}) == v.subs(a, 1).evalf()
|
||||
|
||||
|
||||
def test_lame_coefficients():
|
||||
a = CoordSys3D('a', 'spherical')
|
||||
assert a.lame_coefficients() == (1, a.r, sin(a.theta)*a.r)
|
||||
a = CoordSys3D('a')
|
||||
assert a.lame_coefficients() == (1, 1, 1)
|
||||
a = CoordSys3D('a', 'cartesian')
|
||||
assert a.lame_coefficients() == (1, 1, 1)
|
||||
a = CoordSys3D('a', 'cylindrical')
|
||||
assert a.lame_coefficients() == (1, a.r, 1)
|
||||
|
||||
|
||||
def test_transformation_equations():
|
||||
|
||||
x, y, z = symbols('x y z')
|
||||
# Str
|
||||
a = CoordSys3D('a', transformation='spherical',
|
||||
variable_names=["r", "theta", "phi"])
|
||||
r, theta, phi = a.base_scalars()
|
||||
|
||||
assert r == a.r
|
||||
assert theta == a.theta
|
||||
assert phi == a.phi
|
||||
|
||||
raises(AttributeError, lambda: a.x)
|
||||
raises(AttributeError, lambda: a.y)
|
||||
raises(AttributeError, lambda: a.z)
|
||||
|
||||
assert a.transformation_to_parent() == (
|
||||
r*sin(theta)*cos(phi),
|
||||
r*sin(theta)*sin(phi),
|
||||
r*cos(theta)
|
||||
)
|
||||
assert a.lame_coefficients() == (1, r, r*sin(theta))
|
||||
assert a.transformation_from_parent_function()(x, y, z) == (
|
||||
sqrt(x ** 2 + y ** 2 + z ** 2),
|
||||
acos((z) / sqrt(x**2 + y**2 + z**2)),
|
||||
atan2(y, x)
|
||||
)
|
||||
a = CoordSys3D('a', transformation='cylindrical',
|
||||
variable_names=["r", "theta", "z"])
|
||||
r, theta, z = a.base_scalars()
|
||||
assert a.transformation_to_parent() == (
|
||||
r*cos(theta),
|
||||
r*sin(theta),
|
||||
z
|
||||
)
|
||||
assert a.lame_coefficients() == (1, a.r, 1)
|
||||
assert a.transformation_from_parent_function()(x, y, z) == (sqrt(x**2 + y**2),
|
||||
atan2(y, x), z)
|
||||
|
||||
a = CoordSys3D('a', 'cartesian')
|
||||
assert a.transformation_to_parent() == (a.x, a.y, a.z)
|
||||
assert a.lame_coefficients() == (1, 1, 1)
|
||||
assert a.transformation_from_parent_function()(x, y, z) == (x, y, z)
|
||||
|
||||
# Variables and expressions
|
||||
|
||||
# Cartesian with equation tuple:
|
||||
x, y, z = symbols('x y z')
|
||||
a = CoordSys3D('a', ((x, y, z), (x, y, z)))
|
||||
a._calculate_inv_trans_equations()
|
||||
assert a.transformation_to_parent() == (a.x1, a.x2, a.x3)
|
||||
assert a.lame_coefficients() == (1, 1, 1)
|
||||
assert a.transformation_from_parent_function()(x, y, z) == (x, y, z)
|
||||
r, theta, z = symbols("r theta z")
|
||||
|
||||
# Cylindrical with equation tuple:
|
||||
a = CoordSys3D('a', [(r, theta, z), (r*cos(theta), r*sin(theta), z)],
|
||||
variable_names=["r", "theta", "z"])
|
||||
r, theta, z = a.base_scalars()
|
||||
assert a.transformation_to_parent() == (
|
||||
r*cos(theta), r*sin(theta), z
|
||||
)
|
||||
assert a.lame_coefficients() == (
|
||||
sqrt(sin(theta)**2 + cos(theta)**2),
|
||||
sqrt(r**2*sin(theta)**2 + r**2*cos(theta)**2),
|
||||
1
|
||||
) # ==> this should simplify to (1, r, 1), tests are too slow with `simplify`.
|
||||
|
||||
# Definitions with `lambda`:
|
||||
|
||||
# Cartesian with `lambda`
|
||||
a = CoordSys3D('a', lambda x, y, z: (x, y, z))
|
||||
assert a.transformation_to_parent() == (a.x1, a.x2, a.x3)
|
||||
assert a.lame_coefficients() == (1, 1, 1)
|
||||
a._calculate_inv_trans_equations()
|
||||
assert a.transformation_from_parent_function()(x, y, z) == (x, y, z)
|
||||
|
||||
# Spherical with `lambda`
|
||||
a = CoordSys3D('a', lambda r, theta, phi: (r*sin(theta)*cos(phi), r*sin(theta)*sin(phi), r*cos(theta)),
|
||||
variable_names=["r", "theta", "phi"])
|
||||
r, theta, phi = a.base_scalars()
|
||||
assert a.transformation_to_parent() == (
|
||||
r*sin(theta)*cos(phi), r*sin(phi)*sin(theta), r*cos(theta)
|
||||
)
|
||||
assert a.lame_coefficients() == (
|
||||
sqrt(sin(phi)**2*sin(theta)**2 + sin(theta)**2*cos(phi)**2 + cos(theta)**2),
|
||||
sqrt(r**2*sin(phi)**2*cos(theta)**2 + r**2*sin(theta)**2 + r**2*cos(phi)**2*cos(theta)**2),
|
||||
sqrt(r**2*sin(phi)**2*sin(theta)**2 + r**2*sin(theta)**2*cos(phi)**2)
|
||||
) # ==> this should simplify to (1, r, sin(theta)*r), `simplify` is too slow.
|
||||
|
||||
# Cylindrical with `lambda`
|
||||
a = CoordSys3D('a', lambda r, theta, z:
|
||||
(r*cos(theta), r*sin(theta), z),
|
||||
variable_names=["r", "theta", "z"]
|
||||
)
|
||||
r, theta, z = a.base_scalars()
|
||||
assert a.transformation_to_parent() == (r*cos(theta), r*sin(theta), z)
|
||||
assert a.lame_coefficients() == (
|
||||
sqrt(sin(theta)**2 + cos(theta)**2),
|
||||
sqrt(r**2*sin(theta)**2 + r**2*cos(theta)**2),
|
||||
1
|
||||
) # ==> this should simplify to (1, a.x, 1)
|
||||
|
||||
raises(TypeError, lambda: CoordSys3D('a', transformation={
|
||||
x: x*sin(y)*cos(z), y:x*sin(y)*sin(z), z: x*cos(y)}))
|
||||
|
||||
|
||||
def test_check_orthogonality():
|
||||
x, y, z = symbols('x y z')
|
||||
u,v = symbols('u, v')
|
||||
a = CoordSys3D('a', transformation=((x, y, z), (x*sin(y)*cos(z), x*sin(y)*sin(z), x*cos(y))))
|
||||
assert a._check_orthogonality(a._transformation) is True
|
||||
a = CoordSys3D('a', transformation=((x, y, z), (x * cos(y), x * sin(y), z)))
|
||||
assert a._check_orthogonality(a._transformation) is True
|
||||
a = CoordSys3D('a', transformation=((u, v, z), (cosh(u) * cos(v), sinh(u) * sin(v), z)))
|
||||
assert a._check_orthogonality(a._transformation) is True
|
||||
|
||||
raises(ValueError, lambda: CoordSys3D('a', transformation=((x, y, z), (x, x, z))))
|
||||
raises(ValueError, lambda: CoordSys3D('a', transformation=(
|
||||
(x, y, z), (x*sin(y/2)*cos(z), x*sin(y)*sin(z), x*cos(y)))))
|
||||
|
||||
|
||||
def test_rotation_trans_equations():
|
||||
a = CoordSys3D('a')
|
||||
from sympy.core.symbol import symbols
|
||||
q0 = symbols('q0')
|
||||
assert a._rotation_trans_equations(a._parent_rotation_matrix, a.base_scalars()) == (a.x, a.y, a.z)
|
||||
assert a._rotation_trans_equations(a._inverse_rotation_matrix(), a.base_scalars()) == (a.x, a.y, a.z)
|
||||
b = a.orient_new_axis('b', 0, -a.k)
|
||||
assert b._rotation_trans_equations(b._parent_rotation_matrix, b.base_scalars()) == (b.x, b.y, b.z)
|
||||
assert b._rotation_trans_equations(b._inverse_rotation_matrix(), b.base_scalars()) == (b.x, b.y, b.z)
|
||||
c = a.orient_new_axis('c', q0, -a.k)
|
||||
assert c._rotation_trans_equations(c._parent_rotation_matrix, c.base_scalars()) == \
|
||||
(-sin(q0) * c.y + cos(q0) * c.x, sin(q0) * c.x + cos(q0) * c.y, c.z)
|
||||
assert c._rotation_trans_equations(c._inverse_rotation_matrix(), c.base_scalars()) == \
|
||||
(sin(q0) * c.y + cos(q0) * c.x, -sin(q0) * c.x + cos(q0) * c.y, c.z)
|
||||
@@ -0,0 +1,134 @@
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.vector import (CoordSys3D, Vector, Dyadic,
|
||||
DyadicAdd, DyadicMul, DyadicZero,
|
||||
BaseDyadic, express)
|
||||
|
||||
|
||||
A = CoordSys3D('A')
|
||||
|
||||
|
||||
def test_dyadic():
|
||||
a, b = symbols('a, b')
|
||||
assert Dyadic.zero != 0
|
||||
assert isinstance(Dyadic.zero, DyadicZero)
|
||||
assert BaseDyadic(A.i, A.j) != BaseDyadic(A.j, A.i)
|
||||
assert (BaseDyadic(Vector.zero, A.i) ==
|
||||
BaseDyadic(A.i, Vector.zero) == Dyadic.zero)
|
||||
|
||||
d1 = A.i | A.i
|
||||
d2 = A.j | A.j
|
||||
d3 = A.i | A.j
|
||||
|
||||
assert isinstance(d1, BaseDyadic)
|
||||
d_mul = a*d1
|
||||
assert isinstance(d_mul, DyadicMul)
|
||||
assert d_mul.base_dyadic == d1
|
||||
assert d_mul.measure_number == a
|
||||
assert isinstance(a*d1 + b*d3, DyadicAdd)
|
||||
assert d1 == A.i.outer(A.i)
|
||||
assert d3 == A.i.outer(A.j)
|
||||
v1 = a*A.i - A.k
|
||||
v2 = A.i + b*A.j
|
||||
assert v1 | v2 == v1.outer(v2) == a * (A.i|A.i) + (a*b) * (A.i|A.j) +\
|
||||
- (A.k|A.i) - b * (A.k|A.j)
|
||||
assert d1 * 0 == Dyadic.zero
|
||||
assert d1 != Dyadic.zero
|
||||
assert d1 * 2 == 2 * (A.i | A.i)
|
||||
assert d1 / 2. == 0.5 * d1
|
||||
|
||||
assert d1.dot(0 * d1) == Vector.zero
|
||||
assert d1 & d2 == Dyadic.zero
|
||||
assert d1.dot(A.i) == A.i == d1 & A.i
|
||||
|
||||
assert d1.cross(Vector.zero) == Dyadic.zero
|
||||
assert d1.cross(A.i) == Dyadic.zero
|
||||
assert d1 ^ A.j == d1.cross(A.j)
|
||||
assert d1.cross(A.k) == - A.i | A.j
|
||||
assert d2.cross(A.i) == - A.j | A.k == d2 ^ A.i
|
||||
|
||||
assert A.i ^ d1 == Dyadic.zero
|
||||
assert A.j.cross(d1) == - A.k | A.i == A.j ^ d1
|
||||
assert Vector.zero.cross(d1) == Dyadic.zero
|
||||
assert A.k ^ d1 == A.j | A.i
|
||||
assert A.i.dot(d1) == A.i & d1 == A.i
|
||||
assert A.j.dot(d1) == Vector.zero
|
||||
assert Vector.zero.dot(d1) == Vector.zero
|
||||
assert A.j & d2 == A.j
|
||||
|
||||
assert d1.dot(d3) == d1 & d3 == A.i | A.j == d3
|
||||
assert d3 & d1 == Dyadic.zero
|
||||
|
||||
q = symbols('q')
|
||||
B = A.orient_new_axis('B', q, A.k)
|
||||
assert express(d1, B) == express(d1, B, B)
|
||||
|
||||
expr1 = ((cos(q)**2) * (B.i | B.i) + (-sin(q) * cos(q)) *
|
||||
(B.i | B.j) + (-sin(q) * cos(q)) * (B.j | B.i) + (sin(q)**2) *
|
||||
(B.j | B.j))
|
||||
assert (express(d1, B) - expr1).simplify() == Dyadic.zero
|
||||
|
||||
expr2 = (cos(q)) * (B.i | A.i) + (-sin(q)) * (B.j | A.i)
|
||||
assert (express(d1, B, A) - expr2).simplify() == Dyadic.zero
|
||||
|
||||
expr3 = (cos(q)) * (A.i | B.i) + (-sin(q)) * (A.i | B.j)
|
||||
assert (express(d1, A, B) - expr3).simplify() == Dyadic.zero
|
||||
|
||||
assert d1.to_matrix(A) == Matrix([[1, 0, 0], [0, 0, 0], [0, 0, 0]])
|
||||
assert d1.to_matrix(A, B) == Matrix([[cos(q), -sin(q), 0],
|
||||
[0, 0, 0],
|
||||
[0, 0, 0]])
|
||||
assert d3.to_matrix(A) == Matrix([[0, 1, 0], [0, 0, 0], [0, 0, 0]])
|
||||
a, b, c, d, e, f = symbols('a, b, c, d, e, f')
|
||||
v1 = a * A.i + b * A.j + c * A.k
|
||||
v2 = d * A.i + e * A.j + f * A.k
|
||||
d4 = v1.outer(v2)
|
||||
assert d4.to_matrix(A) == Matrix([[a * d, a * e, a * f],
|
||||
[b * d, b * e, b * f],
|
||||
[c * d, c * e, c * f]])
|
||||
d5 = v1.outer(v1)
|
||||
C = A.orient_new_axis('C', q, A.i)
|
||||
for expected, actual in zip(C.rotation_matrix(A) * d5.to_matrix(A) * \
|
||||
C.rotation_matrix(A).T, d5.to_matrix(C)):
|
||||
assert (expected - actual).simplify() == 0
|
||||
|
||||
|
||||
def test_dyadic_simplify():
|
||||
x, y, z, k, n, m, w, f, s, A = symbols('x, y, z, k, n, m, w, f, s, A')
|
||||
N = CoordSys3D('N')
|
||||
|
||||
dy = N.i | N.i
|
||||
test1 = (1 / x + 1 / y) * dy
|
||||
assert (N.i & test1 & N.i) != (x + y) / (x * y)
|
||||
test1 = test1.simplify()
|
||||
assert test1.simplify() == simplify(test1)
|
||||
assert (N.i & test1 & N.i) == (x + y) / (x * y)
|
||||
|
||||
test2 = (A**2 * s**4 / (4 * pi * k * m**3)) * dy
|
||||
test2 = test2.simplify()
|
||||
assert (N.i & test2 & N.i) == (A**2 * s**4 / (4 * pi * k * m**3))
|
||||
|
||||
test3 = ((4 + 4 * x - 2 * (2 + 2 * x)) / (2 + 2 * x)) * dy
|
||||
test3 = test3.simplify()
|
||||
assert (N.i & test3 & N.i) == 0
|
||||
|
||||
test4 = ((-4 * x * y**2 - 2 * y**3 - 2 * x**2 * y) / (x + y)**2) * dy
|
||||
test4 = test4.simplify()
|
||||
assert (N.i & test4 & N.i) == -2 * y
|
||||
|
||||
|
||||
def test_dyadic_srepr():
|
||||
from sympy.printing.repr import srepr
|
||||
N = CoordSys3D('N')
|
||||
|
||||
dy = N.i | N.j
|
||||
res = "BaseDyadic(CoordSys3D(Str('N'), Tuple(ImmutableDenseMatrix([["\
|
||||
"Integer(1), Integer(0), Integer(0)], [Integer(0), Integer(1), "\
|
||||
"Integer(0)], [Integer(0), Integer(0), Integer(1)]]), "\
|
||||
"VectorZero())).i, CoordSys3D(Str('N'), Tuple(ImmutableDenseMatrix("\
|
||||
"[[Integer(1), Integer(0), Integer(0)], [Integer(0), Integer(1), "\
|
||||
"Integer(0)], [Integer(0), Integer(0), Integer(1)]]), VectorZero())).j)"
|
||||
assert srepr(dy) == res
|
||||
@@ -0,0 +1,321 @@
|
||||
from sympy.core.function import Derivative
|
||||
from sympy.vector.vector import Vector
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.simplify import simplify
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.core import S
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.vector.vector import Dot
|
||||
from sympy.vector.operators import curl, divergence, gradient, Gradient, Divergence, Cross
|
||||
from sympy.vector.deloperator import Del
|
||||
from sympy.vector.functions import (is_conservative, is_solenoidal,
|
||||
scalar_potential, directional_derivative,
|
||||
laplacian, scalar_potential_difference)
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
C = CoordSys3D('C')
|
||||
i, j, k = C.base_vectors()
|
||||
x, y, z = C.base_scalars()
|
||||
delop = Del()
|
||||
a, b, c, q = symbols('a b c q')
|
||||
|
||||
|
||||
def test_del_operator():
|
||||
# Tests for curl
|
||||
|
||||
assert delop ^ Vector.zero == Vector.zero
|
||||
assert ((delop ^ Vector.zero).doit() == Vector.zero ==
|
||||
curl(Vector.zero))
|
||||
assert delop.cross(Vector.zero) == delop ^ Vector.zero
|
||||
assert (delop ^ i).doit() == Vector.zero
|
||||
assert delop.cross(2*y**2*j, doit=True) == Vector.zero
|
||||
assert delop.cross(2*y**2*j) == delop ^ 2*y**2*j
|
||||
v = x*y*z * (i + j + k)
|
||||
assert ((delop ^ v).doit() ==
|
||||
(-x*y + x*z)*i + (x*y - y*z)*j + (-x*z + y*z)*k ==
|
||||
curl(v))
|
||||
assert delop ^ v == delop.cross(v)
|
||||
assert (delop.cross(2*x**2*j) ==
|
||||
(Derivative(0, C.y) - Derivative(2*C.x**2, C.z))*C.i +
|
||||
(-Derivative(0, C.x) + Derivative(0, C.z))*C.j +
|
||||
(-Derivative(0, C.y) + Derivative(2*C.x**2, C.x))*C.k)
|
||||
assert (delop.cross(2*x**2*j, doit=True) == 4*x*k ==
|
||||
curl(2*x**2*j))
|
||||
|
||||
#Tests for divergence
|
||||
assert delop & Vector.zero is S.Zero == divergence(Vector.zero)
|
||||
assert (delop & Vector.zero).doit() is S.Zero
|
||||
assert delop.dot(Vector.zero) == delop & Vector.zero
|
||||
assert (delop & i).doit() is S.Zero
|
||||
assert (delop & x**2*i).doit() == 2*x == divergence(x**2*i)
|
||||
assert (delop.dot(v, doit=True) == x*y + y*z + z*x ==
|
||||
divergence(v))
|
||||
assert delop & v == delop.dot(v)
|
||||
assert delop.dot(1/(x*y*z) * (i + j + k), doit=True) == \
|
||||
- 1 / (x*y*z**2) - 1 / (x*y**2*z) - 1 / (x**2*y*z)
|
||||
v = x*i + y*j + z*k
|
||||
assert (delop & v == Derivative(C.x, C.x) +
|
||||
Derivative(C.y, C.y) + Derivative(C.z, C.z))
|
||||
assert delop.dot(v, doit=True) == 3 == divergence(v)
|
||||
assert delop & v == delop.dot(v)
|
||||
assert simplify((delop & v).doit()) == 3
|
||||
|
||||
#Tests for gradient
|
||||
assert (delop.gradient(0, doit=True) == Vector.zero ==
|
||||
gradient(0))
|
||||
assert delop.gradient(0) == delop(0)
|
||||
assert (delop(S.Zero)).doit() == Vector.zero
|
||||
assert (delop(x) == (Derivative(C.x, C.x))*C.i +
|
||||
(Derivative(C.x, C.y))*C.j + (Derivative(C.x, C.z))*C.k)
|
||||
assert (delop(x)).doit() == i == gradient(x)
|
||||
assert (delop(x*y*z) ==
|
||||
(Derivative(C.x*C.y*C.z, C.x))*C.i +
|
||||
(Derivative(C.x*C.y*C.z, C.y))*C.j +
|
||||
(Derivative(C.x*C.y*C.z, C.z))*C.k)
|
||||
assert (delop.gradient(x*y*z, doit=True) ==
|
||||
y*z*i + z*x*j + x*y*k ==
|
||||
gradient(x*y*z))
|
||||
assert delop(x*y*z) == delop.gradient(x*y*z)
|
||||
assert (delop(2*x**2)).doit() == 4*x*i
|
||||
assert ((delop(a*sin(y) / x)).doit() ==
|
||||
-a*sin(y)/x**2 * i + a*cos(y)/x * j)
|
||||
|
||||
#Tests for directional derivative
|
||||
assert (Vector.zero & delop)(a) is S.Zero
|
||||
assert ((Vector.zero & delop)(a)).doit() is S.Zero
|
||||
assert ((v & delop)(Vector.zero)).doit() == Vector.zero
|
||||
assert ((v & delop)(S.Zero)).doit() is S.Zero
|
||||
assert ((i & delop)(x)).doit() == 1
|
||||
assert ((j & delop)(y)).doit() == 1
|
||||
assert ((k & delop)(z)).doit() == 1
|
||||
assert ((i & delop)(x*y*z)).doit() == y*z
|
||||
assert ((v & delop)(x)).doit() == x
|
||||
assert ((v & delop)(x*y*z)).doit() == 3*x*y*z
|
||||
assert (v & delop)(x + y + z) == C.x + C.y + C.z
|
||||
assert ((v & delop)(x + y + z)).doit() == x + y + z
|
||||
assert ((v & delop)(v)).doit() == v
|
||||
assert ((i & delop)(v)).doit() == i
|
||||
assert ((j & delop)(v)).doit() == j
|
||||
assert ((k & delop)(v)).doit() == k
|
||||
assert ((v & delop)(Vector.zero)).doit() == Vector.zero
|
||||
|
||||
# Tests for laplacian on scalar fields
|
||||
assert laplacian(x*y*z) is S.Zero
|
||||
assert laplacian(x**2) == S(2)
|
||||
assert laplacian(x**2*y**2*z**2) == \
|
||||
2*y**2*z**2 + 2*x**2*z**2 + 2*x**2*y**2
|
||||
A = CoordSys3D('A', transformation="spherical", variable_names=["r", "theta", "phi"])
|
||||
B = CoordSys3D('B', transformation='cylindrical', variable_names=["r", "theta", "z"])
|
||||
assert laplacian(A.r + A.theta + A.phi) == 2/A.r + cos(A.theta)/(A.r**2*sin(A.theta))
|
||||
assert laplacian(B.r + B.theta + B.z) == 1/B.r
|
||||
|
||||
# Tests for laplacian on vector fields
|
||||
assert laplacian(x*y*z*(i + j + k)) == Vector.zero
|
||||
assert laplacian(x*y**2*z*(i + j + k)) == \
|
||||
2*x*z*i + 2*x*z*j + 2*x*z*k
|
||||
|
||||
|
||||
def test_product_rules():
|
||||
"""
|
||||
Tests the six product rules defined with respect to the Del
|
||||
operator
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Del
|
||||
|
||||
"""
|
||||
|
||||
#Define the scalar and vector functions
|
||||
f = 2*x*y*z
|
||||
g = x*y + y*z + z*x
|
||||
u = x**2*i + 4*j - y**2*z*k
|
||||
v = 4*i + x*y*z*k
|
||||
|
||||
# First product rule
|
||||
lhs = delop(f * g, doit=True)
|
||||
rhs = (f * delop(g) + g * delop(f)).doit()
|
||||
assert simplify(lhs) == simplify(rhs)
|
||||
|
||||
# Second product rule
|
||||
lhs = delop(u & v).doit()
|
||||
rhs = ((u ^ (delop ^ v)) + (v ^ (delop ^ u)) + \
|
||||
((u & delop)(v)) + ((v & delop)(u))).doit()
|
||||
assert simplify(lhs) == simplify(rhs)
|
||||
|
||||
# Third product rule
|
||||
lhs = (delop & (f*v)).doit()
|
||||
rhs = ((f * (delop & v)) + (v & (delop(f)))).doit()
|
||||
assert simplify(lhs) == simplify(rhs)
|
||||
|
||||
# Fourth product rule
|
||||
lhs = (delop & (u ^ v)).doit()
|
||||
rhs = ((v & (delop ^ u)) - (u & (delop ^ v))).doit()
|
||||
assert simplify(lhs) == simplify(rhs)
|
||||
|
||||
# Fifth product rule
|
||||
lhs = (delop ^ (f * v)).doit()
|
||||
rhs = (((delop(f)) ^ v) + (f * (delop ^ v))).doit()
|
||||
assert simplify(lhs) == simplify(rhs)
|
||||
|
||||
# Sixth product rule
|
||||
lhs = (delop ^ (u ^ v)).doit()
|
||||
rhs = (u * (delop & v) - v * (delop & u) +
|
||||
(v & delop)(u) - (u & delop)(v)).doit()
|
||||
assert simplify(lhs) == simplify(rhs)
|
||||
|
||||
|
||||
P = C.orient_new_axis('P', q, C.k) # type: ignore
|
||||
scalar_field = 2*x**2*y*z
|
||||
grad_field = gradient(scalar_field)
|
||||
vector_field = y**2*i + 3*x*j + 5*y*z*k
|
||||
curl_field = curl(vector_field)
|
||||
|
||||
|
||||
def test_conservative():
|
||||
assert is_conservative(Vector.zero) is True
|
||||
assert is_conservative(i) is True
|
||||
assert is_conservative(2 * i + 3 * j + 4 * k) is True
|
||||
assert (is_conservative(y*z*i + x*z*j + x*y*k) is
|
||||
True)
|
||||
assert is_conservative(x * j) is False
|
||||
assert is_conservative(grad_field) is True
|
||||
assert is_conservative(curl_field) is False
|
||||
assert (is_conservative(4*x*y*z*i + 2*x**2*z*j) is
|
||||
False)
|
||||
assert is_conservative(z*P.i + P.x*k) is True
|
||||
|
||||
|
||||
def test_solenoidal():
|
||||
assert is_solenoidal(Vector.zero) is True
|
||||
assert is_solenoidal(i) is True
|
||||
assert is_solenoidal(2 * i + 3 * j + 4 * k) is True
|
||||
assert (is_solenoidal(y*z*i + x*z*j + x*y*k) is
|
||||
True)
|
||||
assert is_solenoidal(y * j) is False
|
||||
assert is_solenoidal(grad_field) is False
|
||||
assert is_solenoidal(curl_field) is True
|
||||
assert is_solenoidal((-2*y + 3)*k) is True
|
||||
assert is_solenoidal(cos(q)*i + sin(q)*j + cos(q)*P.k) is True
|
||||
assert is_solenoidal(z*P.i + P.x*k) is True
|
||||
|
||||
|
||||
def test_directional_derivative():
|
||||
assert directional_derivative(C.x*C.y*C.z, 3*C.i + 4*C.j + C.k) == C.x*C.y + 4*C.x*C.z + 3*C.y*C.z
|
||||
assert directional_derivative(5*C.x**2*C.z, 3*C.i + 4*C.j + C.k) == 5*C.x**2 + 30*C.x*C.z
|
||||
assert directional_derivative(5*C.x**2*C.z, 4*C.j) is S.Zero
|
||||
|
||||
D = CoordSys3D("D", "spherical", variable_names=["r", "theta", "phi"],
|
||||
vector_names=["e_r", "e_theta", "e_phi"])
|
||||
r, theta, phi = D.base_scalars()
|
||||
e_r, e_theta, e_phi = D.base_vectors()
|
||||
assert directional_derivative(r**2*e_r, e_r) == 2*r*e_r
|
||||
assert directional_derivative(5*r**2*phi, 3*e_r + 4*e_theta + e_phi) == 5*r**2 + 30*r*phi
|
||||
|
||||
|
||||
def test_scalar_potential():
|
||||
assert scalar_potential(Vector.zero, C) == 0
|
||||
assert scalar_potential(i, C) == x
|
||||
assert scalar_potential(j, C) == y
|
||||
assert scalar_potential(k, C) == z
|
||||
assert scalar_potential(y*z*i + x*z*j + x*y*k, C) == x*y*z
|
||||
assert scalar_potential(grad_field, C) == scalar_field
|
||||
assert scalar_potential(z*P.i + P.x*k, C) == x*z*cos(q) + y*z*sin(q)
|
||||
assert scalar_potential(z*P.i + P.x*k, P) == P.x*P.z
|
||||
raises(ValueError, lambda: scalar_potential(x*j, C))
|
||||
|
||||
|
||||
def test_scalar_potential_difference():
|
||||
point1 = C.origin.locate_new('P1', 1*i + 2*j + 3*k)
|
||||
point2 = C.origin.locate_new('P2', 4*i + 5*j + 6*k)
|
||||
genericpointC = C.origin.locate_new('RP', x*i + y*j + z*k)
|
||||
genericpointP = P.origin.locate_new('PP', P.x*P.i + P.y*P.j + P.z*P.k)
|
||||
assert scalar_potential_difference(S.Zero, C, point1, point2) == 0
|
||||
assert (scalar_potential_difference(scalar_field, C, C.origin,
|
||||
genericpointC) ==
|
||||
scalar_field)
|
||||
assert (scalar_potential_difference(grad_field, C, C.origin,
|
||||
genericpointC) ==
|
||||
scalar_field)
|
||||
assert scalar_potential_difference(grad_field, C, point1, point2) == 948
|
||||
assert (scalar_potential_difference(y*z*i + x*z*j +
|
||||
x*y*k, C, point1,
|
||||
genericpointC) ==
|
||||
x*y*z - 6)
|
||||
potential_diff_P = (2*P.z*(P.x*sin(q) + P.y*cos(q))*
|
||||
(P.x*cos(q) - P.y*sin(q))**2)
|
||||
assert (scalar_potential_difference(grad_field, P, P.origin,
|
||||
genericpointP).simplify() ==
|
||||
potential_diff_P.simplify())
|
||||
|
||||
|
||||
def test_differential_operators_curvilinear_system():
|
||||
A = CoordSys3D('A', transformation="spherical", variable_names=["r", "theta", "phi"])
|
||||
B = CoordSys3D('B', transformation='cylindrical', variable_names=["r", "theta", "z"])
|
||||
# Test for spherical coordinate system and gradient
|
||||
assert gradient(3*A.r + 4*A.theta) == 3*A.i + 4/A.r*A.j
|
||||
assert gradient(3*A.r*A.phi + 4*A.theta) == 3*A.phi*A.i + 4/A.r*A.j + (3/sin(A.theta))*A.k
|
||||
assert gradient(0*A.r + 0*A.theta+0*A.phi) == Vector.zero
|
||||
assert gradient(A.r*A.theta*A.phi) == A.theta*A.phi*A.i + A.phi*A.j + (A.theta/sin(A.theta))*A.k
|
||||
# Test for spherical coordinate system and divergence
|
||||
assert divergence(A.r * A.i + A.theta * A.j + A.phi * A.k) == \
|
||||
(sin(A.theta)*A.r + cos(A.theta)*A.r*A.theta)/(sin(A.theta)*A.r**2) + 3 + 1/(sin(A.theta)*A.r)
|
||||
assert divergence(3*A.r*A.phi*A.i + A.theta*A.j + A.r*A.theta*A.phi*A.k) == \
|
||||
(sin(A.theta)*A.r + cos(A.theta)*A.r*A.theta)/(sin(A.theta)*A.r**2) + 9*A.phi + A.theta/sin(A.theta)
|
||||
assert divergence(Vector.zero) == 0
|
||||
assert divergence(0*A.i + 0*A.j + 0*A.k) == 0
|
||||
# Test for spherical coordinate system and curl
|
||||
assert curl(A.r*A.i + A.theta*A.j + A.phi*A.k) == \
|
||||
(cos(A.theta)*A.phi/(sin(A.theta)*A.r))*A.i + (-A.phi/A.r)*A.j + A.theta/A.r*A.k
|
||||
assert curl(A.r*A.j + A.phi*A.k) == (cos(A.theta)*A.phi/(sin(A.theta)*A.r))*A.i + (-A.phi/A.r)*A.j + 2*A.k
|
||||
|
||||
# Test for cylindrical coordinate system and gradient
|
||||
assert gradient(0*B.r + 0*B.theta+0*B.z) == Vector.zero
|
||||
assert gradient(B.r*B.theta*B.z) == B.theta*B.z*B.i + B.z*B.j + B.r*B.theta*B.k
|
||||
assert gradient(3*B.r) == 3*B.i
|
||||
assert gradient(2*B.theta) == 2/B.r * B.j
|
||||
assert gradient(4*B.z) == 4*B.k
|
||||
# Test for cylindrical coordinate system and divergence
|
||||
assert divergence(B.r*B.i + B.theta*B.j + B.z*B.k) == 3 + 1/B.r
|
||||
assert divergence(B.r*B.j + B.z*B.k) == 1
|
||||
# Test for cylindrical coordinate system and curl
|
||||
assert curl(B.r*B.j + B.z*B.k) == 2*B.k
|
||||
assert curl(3*B.i + 2/B.r*B.j + 4*B.k) == Vector.zero
|
||||
|
||||
def test_mixed_coordinates():
|
||||
# gradient
|
||||
a = CoordSys3D('a')
|
||||
b = CoordSys3D('b')
|
||||
c = CoordSys3D('c')
|
||||
assert gradient(a.x*b.y) == b.y*a.i + a.x*b.j
|
||||
assert gradient(3*cos(q)*a.x*b.x+a.y*(a.x+(cos(q)+b.x))) ==\
|
||||
(a.y + 3*b.x*cos(q))*a.i + (a.x + b.x + cos(q))*a.j + (3*a.x*cos(q) + a.y)*b.i
|
||||
# Some tests need further work:
|
||||
# assert gradient(a.x*(cos(a.x+b.x))) == (cos(a.x + b.x))*a.i + a.x*Gradient(cos(a.x + b.x))
|
||||
# assert gradient(cos(a.x + b.x)*cos(a.x + b.z)) == Gradient(cos(a.x + b.x)*cos(a.x + b.z))
|
||||
assert gradient(a.x**b.y) == Gradient(a.x**b.y)
|
||||
# assert gradient(cos(a.x+b.y)*a.z) == None
|
||||
assert gradient(cos(a.x*b.y)) == Gradient(cos(a.x*b.y))
|
||||
assert gradient(3*cos(q)*a.x*b.x*a.z*a.y+ b.y*b.z + cos(a.x+a.y)*b.z) == \
|
||||
(3*a.y*a.z*b.x*cos(q) - b.z*sin(a.x + a.y))*a.i + \
|
||||
(3*a.x*a.z*b.x*cos(q) - b.z*sin(a.x + a.y))*a.j + (3*a.x*a.y*b.x*cos(q))*a.k + \
|
||||
(3*a.x*a.y*a.z*cos(q))*b.i + b.z*b.j + (b.y + cos(a.x + a.y))*b.k
|
||||
# divergence
|
||||
assert divergence(a.i*a.x+a.j*a.y+a.z*a.k + b.i*b.x+b.j*b.y+b.z*b.k + c.i*c.x+c.j*c.y+c.z*c.k) == S(9)
|
||||
# assert divergence(3*a.i*a.x*cos(a.x+b.z) + a.j*b.x*c.z) == None
|
||||
assert divergence(3*a.i*a.x*a.z + b.j*b.x*c.z + 3*a.j*a.z*a.y) == \
|
||||
6*a.z + b.x*Dot(b.j, c.k)
|
||||
assert divergence(3*cos(q)*a.x*b.x*b.i*c.x) == \
|
||||
3*a.x*b.x*cos(q)*Dot(b.i, c.i) + 3*a.x*c.x*cos(q) + 3*b.x*c.x*cos(q)*Dot(b.i, a.i)
|
||||
assert divergence(a.x*b.x*c.x*Cross(a.x*a.i, a.y*b.j)) ==\
|
||||
a.x*b.x*c.x*Divergence(Cross(a.x*a.i, a.y*b.j)) + \
|
||||
b.x*c.x*Dot(Cross(a.x*a.i, a.y*b.j), a.i) + \
|
||||
a.x*c.x*Dot(Cross(a.x*a.i, a.y*b.j), b.i) + \
|
||||
a.x*b.x*Dot(Cross(a.x*a.i, a.y*b.j), c.i)
|
||||
assert divergence(a.x*b.x*c.x*(a.x*a.i + b.x*b.i)) == \
|
||||
4*a.x*b.x*c.x +\
|
||||
a.x**2*c.x*Dot(a.i, b.i) +\
|
||||
a.x**2*b.x*Dot(a.i, c.i) +\
|
||||
b.x**2*c.x*Dot(b.i, a.i) +\
|
||||
a.x*b.x**2*Dot(b.i, c.i)
|
||||
@@ -0,0 +1,184 @@
|
||||
from sympy.vector.vector import Vector
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.functions import express, matrix_to_vector, orthogonalize
|
||||
from sympy.core.numbers import Rational
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
N = CoordSys3D('N')
|
||||
q1, q2, q3, q4, q5 = symbols('q1 q2 q3 q4 q5')
|
||||
A = N.orient_new_axis('A', q1, N.k) # type: ignore
|
||||
B = A.orient_new_axis('B', q2, A.i)
|
||||
C = B.orient_new_axis('C', q3, B.j)
|
||||
|
||||
|
||||
def test_express():
|
||||
assert express(Vector.zero, N) == Vector.zero
|
||||
assert express(S.Zero, N) is S.Zero
|
||||
assert express(A.i, C) == cos(q3)*C.i + sin(q3)*C.k
|
||||
assert express(A.j, C) == sin(q2)*sin(q3)*C.i + cos(q2)*C.j - \
|
||||
sin(q2)*cos(q3)*C.k
|
||||
assert express(A.k, C) == -sin(q3)*cos(q2)*C.i + sin(q2)*C.j + \
|
||||
cos(q2)*cos(q3)*C.k
|
||||
assert express(A.i, N) == cos(q1)*N.i + sin(q1)*N.j
|
||||
assert express(A.j, N) == -sin(q1)*N.i + cos(q1)*N.j
|
||||
assert express(A.k, N) == N.k
|
||||
assert express(A.i, A) == A.i
|
||||
assert express(A.j, A) == A.j
|
||||
assert express(A.k, A) == A.k
|
||||
assert express(A.i, B) == B.i
|
||||
assert express(A.j, B) == cos(q2)*B.j - sin(q2)*B.k
|
||||
assert express(A.k, B) == sin(q2)*B.j + cos(q2)*B.k
|
||||
assert express(A.i, C) == cos(q3)*C.i + sin(q3)*C.k
|
||||
assert express(A.j, C) == sin(q2)*sin(q3)*C.i + cos(q2)*C.j - \
|
||||
sin(q2)*cos(q3)*C.k
|
||||
assert express(A.k, C) == -sin(q3)*cos(q2)*C.i + sin(q2)*C.j + \
|
||||
cos(q2)*cos(q3)*C.k
|
||||
# Check to make sure UnitVectors get converted properly
|
||||
assert express(N.i, N) == N.i
|
||||
assert express(N.j, N) == N.j
|
||||
assert express(N.k, N) == N.k
|
||||
assert express(N.i, A) == (cos(q1)*A.i - sin(q1)*A.j)
|
||||
assert express(N.j, A) == (sin(q1)*A.i + cos(q1)*A.j)
|
||||
assert express(N.k, A) == A.k
|
||||
assert express(N.i, B) == (cos(q1)*B.i - sin(q1)*cos(q2)*B.j +
|
||||
sin(q1)*sin(q2)*B.k)
|
||||
assert express(N.j, B) == (sin(q1)*B.i + cos(q1)*cos(q2)*B.j -
|
||||
sin(q2)*cos(q1)*B.k)
|
||||
assert express(N.k, B) == (sin(q2)*B.j + cos(q2)*B.k)
|
||||
assert express(N.i, C) == (
|
||||
(cos(q1)*cos(q3) - sin(q1)*sin(q2)*sin(q3))*C.i -
|
||||
sin(q1)*cos(q2)*C.j +
|
||||
(sin(q3)*cos(q1) + sin(q1)*sin(q2)*cos(q3))*C.k)
|
||||
assert express(N.j, C) == (
|
||||
(sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*C.i +
|
||||
cos(q1)*cos(q2)*C.j +
|
||||
(sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*C.k)
|
||||
assert express(N.k, C) == (-sin(q3)*cos(q2)*C.i + sin(q2)*C.j +
|
||||
cos(q2)*cos(q3)*C.k)
|
||||
|
||||
assert express(A.i, N) == (cos(q1)*N.i + sin(q1)*N.j)
|
||||
assert express(A.j, N) == (-sin(q1)*N.i + cos(q1)*N.j)
|
||||
assert express(A.k, N) == N.k
|
||||
assert express(A.i, A) == A.i
|
||||
assert express(A.j, A) == A.j
|
||||
assert express(A.k, A) == A.k
|
||||
assert express(A.i, B) == B.i
|
||||
assert express(A.j, B) == (cos(q2)*B.j - sin(q2)*B.k)
|
||||
assert express(A.k, B) == (sin(q2)*B.j + cos(q2)*B.k)
|
||||
assert express(A.i, C) == (cos(q3)*C.i + sin(q3)*C.k)
|
||||
assert express(A.j, C) == (sin(q2)*sin(q3)*C.i + cos(q2)*C.j -
|
||||
sin(q2)*cos(q3)*C.k)
|
||||
assert express(A.k, C) == (-sin(q3)*cos(q2)*C.i + sin(q2)*C.j +
|
||||
cos(q2)*cos(q3)*C.k)
|
||||
|
||||
assert express(B.i, N) == (cos(q1)*N.i + sin(q1)*N.j)
|
||||
assert express(B.j, N) == (-sin(q1)*cos(q2)*N.i +
|
||||
cos(q1)*cos(q2)*N.j + sin(q2)*N.k)
|
||||
assert express(B.k, N) == (sin(q1)*sin(q2)*N.i -
|
||||
sin(q2)*cos(q1)*N.j + cos(q2)*N.k)
|
||||
assert express(B.i, A) == A.i
|
||||
assert express(B.j, A) == (cos(q2)*A.j + sin(q2)*A.k)
|
||||
assert express(B.k, A) == (-sin(q2)*A.j + cos(q2)*A.k)
|
||||
assert express(B.i, B) == B.i
|
||||
assert express(B.j, B) == B.j
|
||||
assert express(B.k, B) == B.k
|
||||
assert express(B.i, C) == (cos(q3)*C.i + sin(q3)*C.k)
|
||||
assert express(B.j, C) == C.j
|
||||
assert express(B.k, C) == (-sin(q3)*C.i + cos(q3)*C.k)
|
||||
|
||||
assert express(C.i, N) == (
|
||||
(cos(q1)*cos(q3) - sin(q1)*sin(q2)*sin(q3))*N.i +
|
||||
(sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1))*N.j -
|
||||
sin(q3)*cos(q2)*N.k)
|
||||
assert express(C.j, N) == (
|
||||
-sin(q1)*cos(q2)*N.i + cos(q1)*cos(q2)*N.j + sin(q2)*N.k)
|
||||
assert express(C.k, N) == (
|
||||
(sin(q3)*cos(q1) + sin(q1)*sin(q2)*cos(q3))*N.i +
|
||||
(sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3))*N.j +
|
||||
cos(q2)*cos(q3)*N.k)
|
||||
assert express(C.i, A) == (cos(q3)*A.i + sin(q2)*sin(q3)*A.j -
|
||||
sin(q3)*cos(q2)*A.k)
|
||||
assert express(C.j, A) == (cos(q2)*A.j + sin(q2)*A.k)
|
||||
assert express(C.k, A) == (sin(q3)*A.i - sin(q2)*cos(q3)*A.j +
|
||||
cos(q2)*cos(q3)*A.k)
|
||||
assert express(C.i, B) == (cos(q3)*B.i - sin(q3)*B.k)
|
||||
assert express(C.j, B) == B.j
|
||||
assert express(C.k, B) == (sin(q3)*B.i + cos(q3)*B.k)
|
||||
assert express(C.i, C) == C.i
|
||||
assert express(C.j, C) == C.j
|
||||
assert express(C.k, C) == C.k == (C.k)
|
||||
|
||||
# Check to make sure Vectors get converted back to UnitVectors
|
||||
assert N.i == express((cos(q1)*A.i - sin(q1)*A.j), N).simplify()
|
||||
assert N.j == express((sin(q1)*A.i + cos(q1)*A.j), N).simplify()
|
||||
assert N.i == express((cos(q1)*B.i - sin(q1)*cos(q2)*B.j +
|
||||
sin(q1)*sin(q2)*B.k), N).simplify()
|
||||
assert N.j == express((sin(q1)*B.i + cos(q1)*cos(q2)*B.j -
|
||||
sin(q2)*cos(q1)*B.k), N).simplify()
|
||||
assert N.k == express((sin(q2)*B.j + cos(q2)*B.k), N).simplify()
|
||||
|
||||
|
||||
assert A.i == express((cos(q1)*N.i + sin(q1)*N.j), A).simplify()
|
||||
assert A.j == express((-sin(q1)*N.i + cos(q1)*N.j), A).simplify()
|
||||
|
||||
assert A.j == express((cos(q2)*B.j - sin(q2)*B.k), A).simplify()
|
||||
assert A.k == express((sin(q2)*B.j + cos(q2)*B.k), A).simplify()
|
||||
|
||||
assert A.i == express((cos(q3)*C.i + sin(q3)*C.k), A).simplify()
|
||||
assert A.j == express((sin(q2)*sin(q3)*C.i + cos(q2)*C.j -
|
||||
sin(q2)*cos(q3)*C.k), A).simplify()
|
||||
|
||||
assert A.k == express((-sin(q3)*cos(q2)*C.i + sin(q2)*C.j +
|
||||
cos(q2)*cos(q3)*C.k), A).simplify()
|
||||
assert B.i == express((cos(q1)*N.i + sin(q1)*N.j), B).simplify()
|
||||
assert B.j == express((-sin(q1)*cos(q2)*N.i +
|
||||
cos(q1)*cos(q2)*N.j + sin(q2)*N.k), B).simplify()
|
||||
|
||||
assert B.k == express((sin(q1)*sin(q2)*N.i -
|
||||
sin(q2)*cos(q1)*N.j + cos(q2)*N.k), B).simplify()
|
||||
|
||||
assert B.j == express((cos(q2)*A.j + sin(q2)*A.k), B).simplify()
|
||||
assert B.k == express((-sin(q2)*A.j + cos(q2)*A.k), B).simplify()
|
||||
assert B.i == express((cos(q3)*C.i + sin(q3)*C.k), B).simplify()
|
||||
assert B.k == express((-sin(q3)*C.i + cos(q3)*C.k), B).simplify()
|
||||
assert C.i == express((cos(q3)*A.i + sin(q2)*sin(q3)*A.j -
|
||||
sin(q3)*cos(q2)*A.k), C).simplify()
|
||||
assert C.j == express((cos(q2)*A.j + sin(q2)*A.k), C).simplify()
|
||||
assert C.k == express((sin(q3)*A.i - sin(q2)*cos(q3)*A.j +
|
||||
cos(q2)*cos(q3)*A.k), C).simplify()
|
||||
assert C.i == express((cos(q3)*B.i - sin(q3)*B.k), C).simplify()
|
||||
assert C.k == express((sin(q3)*B.i + cos(q3)*B.k), C).simplify()
|
||||
|
||||
|
||||
def test_matrix_to_vector():
|
||||
m = Matrix([[1], [2], [3]])
|
||||
assert matrix_to_vector(m, C) == C.i + 2*C.j + 3*C.k
|
||||
m = Matrix([[0], [0], [0]])
|
||||
assert matrix_to_vector(m, N) == matrix_to_vector(m, C) == \
|
||||
Vector.zero
|
||||
m = Matrix([[q1], [q2], [q3]])
|
||||
assert matrix_to_vector(m, N) == q1*N.i + q2*N.j + q3*N.k
|
||||
|
||||
|
||||
def test_orthogonalize():
|
||||
C = CoordSys3D('C')
|
||||
a, b = symbols('a b', integer=True)
|
||||
i, j, k = C.base_vectors()
|
||||
v1 = i + 2*j
|
||||
v2 = 2*i + 3*j
|
||||
v3 = 3*i + 5*j
|
||||
v4 = 3*i + j
|
||||
v5 = 2*i + 2*j
|
||||
v6 = a*i + b*j
|
||||
v7 = 4*a*i + 4*b*j
|
||||
assert orthogonalize(v1, v2) == [C.i + 2*C.j, C.i*Rational(2, 5) + -C.j/5]
|
||||
# from wikipedia
|
||||
assert orthogonalize(v4, v5, orthonormal=True) == \
|
||||
[(3*sqrt(10))*C.i/10 + (sqrt(10))*C.j/10, (-sqrt(10))*C.i/10 + (3*sqrt(10))*C.j/10]
|
||||
raises(ValueError, lambda: orthogonalize(v1, v2, v3))
|
||||
raises(ValueError, lambda: orthogonalize(v6, v7))
|
||||
@@ -0,0 +1,90 @@
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.abc import x, y, z, s, t
|
||||
from sympy.sets import FiniteSet, EmptySet
|
||||
from sympy.geometry import Point
|
||||
from sympy.vector import ImplicitRegion
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
|
||||
def test_ImplicitRegion():
|
||||
ellipse = ImplicitRegion((x, y), (x**2/4 + y**2/16 - 1))
|
||||
assert ellipse.equation == x**2/4 + y**2/16 - 1
|
||||
assert ellipse.variables == (x, y)
|
||||
assert ellipse.degree == 2
|
||||
r = ImplicitRegion((x, y, z), Eq(x**4 + y**2 - x*y, 6))
|
||||
assert r.equation == x**4 + y**2 - x*y - 6
|
||||
assert r.variables == (x, y, z)
|
||||
assert r.degree == 4
|
||||
|
||||
|
||||
def test_regular_point():
|
||||
r1 = ImplicitRegion((x,), x**2 - 16)
|
||||
assert r1.regular_point() == (-4,)
|
||||
c1 = ImplicitRegion((x, y), x**2 + y**2 - 4)
|
||||
assert c1.regular_point() == (0, -2)
|
||||
c2 = ImplicitRegion((x, y), (x - S(5)/2)**2 + y**2 - (S(1)/4)**2)
|
||||
assert c2.regular_point() == (S(5)/2, -S(1)/4)
|
||||
c3 = ImplicitRegion((x, y), (y - 5)**2 - 16*(x - 5))
|
||||
assert c3.regular_point() == (5, 5)
|
||||
r2 = ImplicitRegion((x, y), x**2 - 4*x*y - 3*y**2 + 4*x + 8*y - 5)
|
||||
assert r2.regular_point() == (S(4)/7, S(9)/7)
|
||||
r3 = ImplicitRegion((x, y), x**2 - 2*x*y + 3*y**2 - 2*x - 5*y + 3/2)
|
||||
raises(ValueError, lambda: r3.regular_point())
|
||||
|
||||
|
||||
def test_singular_points_and_multiplicty():
|
||||
r1 = ImplicitRegion((x, y, z), Eq(x + y + z, 0))
|
||||
assert r1.singular_points() == EmptySet
|
||||
r2 = ImplicitRegion((x, y, z), x*y*z + y**4 -x**2*z**2)
|
||||
assert r2.singular_points() == FiniteSet((0, 0, z), (x, 0, 0))
|
||||
assert r2.multiplicity((0, 0, 0)) == 3
|
||||
assert r2.multiplicity((0, 0, 6)) == 2
|
||||
r3 = ImplicitRegion((x, y, z), z**2 - x**2 - y**2)
|
||||
assert r3.singular_points() == FiniteSet((0, 0, 0))
|
||||
assert r3.multiplicity((0, 0, 0)) == 2
|
||||
r4 = ImplicitRegion((x, y), x**2 + y**2 - 2*x)
|
||||
assert r4.singular_points() == EmptySet
|
||||
assert r4.multiplicity(Point(1, 3)) == 0
|
||||
|
||||
|
||||
def test_rational_parametrization():
|
||||
p = ImplicitRegion((x,), x - 2)
|
||||
assert p.rational_parametrization() == (x - 2,)
|
||||
|
||||
line = ImplicitRegion((x, y), Eq(y, 3*x + 2))
|
||||
assert line.rational_parametrization() == (x, 3*x + 2)
|
||||
|
||||
circle1 = ImplicitRegion((x, y), (x-2)**2 + (y+3)**2 - 4)
|
||||
assert circle1.rational_parametrization(parameters=t) == (4*t/(t**2 + 1) + 2, 4*t**2/(t**2 + 1) - 5)
|
||||
circle2 = ImplicitRegion((x, y), (x - S.Half)**2 + y**2 - (S(1)/2)**2)
|
||||
|
||||
assert circle2.rational_parametrization(parameters=t) == (t/(t**2 + 1) + S(1)/2, t**2/(t**2 + 1) - S(1)/2)
|
||||
circle3 = ImplicitRegion((x, y), Eq(x**2 + y**2, 2*x))
|
||||
assert circle3.rational_parametrization(parameters=(t,)) == (2*t/(t**2 + 1) + 1, 2*t**2/(t**2 + 1) - 1)
|
||||
|
||||
parabola = ImplicitRegion((x, y), (y - 3)**2 - 4*(x + 6))
|
||||
assert parabola.rational_parametrization(t) == (-6 + 4/t**2, 3 + 4/t)
|
||||
|
||||
rect_hyperbola = ImplicitRegion((x, y), x*y - 1)
|
||||
assert rect_hyperbola.rational_parametrization(t) == (-1 + (t + 1)/t, t)
|
||||
|
||||
cubic_curve = ImplicitRegion((x, y), x**3 + x**2 - y**2)
|
||||
assert cubic_curve.rational_parametrization(parameters=(t)) == (t**2 - 1, t*(t**2 - 1))
|
||||
cuspidal = ImplicitRegion((x, y), (x**3 - y**2))
|
||||
assert cuspidal.rational_parametrization(t) == (t**2, t**3)
|
||||
|
||||
I = ImplicitRegion((x, y), x**3 + x**2 - y**2)
|
||||
assert I.rational_parametrization(t) == (t**2 - 1, t*(t**2 - 1))
|
||||
|
||||
sphere = ImplicitRegion((x, y, z), Eq(x**2 + y**2 + z**2, 2*x))
|
||||
assert sphere.rational_parametrization(parameters=(s, t)) == (2/(s**2 + t**2 + 1), 2*t/(s**2 + t**2 + 1), 2*s/(s**2 + t**2 + 1))
|
||||
|
||||
conic = ImplicitRegion((x, y), Eq(x**2 + 4*x*y + 3*y**2 + x - y + 10, 0))
|
||||
assert conic.rational_parametrization(t) == (
|
||||
S(17)/2 + 4/(3*t**2 + 4*t + 1), 4*t/(3*t**2 + 4*t + 1) - S(11)/2)
|
||||
|
||||
r1 = ImplicitRegion((x, y), y**2 - x**3 + x)
|
||||
raises(NotImplementedError, lambda: r1.rational_parametrization())
|
||||
r2 = ImplicitRegion((x, y), y**2 - x**3 - x**2 + 1)
|
||||
raises(NotImplementedError, lambda: r2.rational_parametrization())
|
||||
@@ -0,0 +1,106 @@
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.core.singleton import S
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.integrals import ParametricIntegral, vector_integrate
|
||||
from sympy.vector.parametricregion import ParametricRegion
|
||||
from sympy.vector.implicitregion import ImplicitRegion
|
||||
from sympy.abc import x, y, z, u, v, r, t, theta, phi
|
||||
from sympy.geometry import Point, Segment, Curve, Circle, Polygon, Plane
|
||||
|
||||
C = CoordSys3D('C')
|
||||
|
||||
def test_parametric_lineintegrals():
|
||||
halfcircle = ParametricRegion((4*cos(theta), 4*sin(theta)), (theta, -pi/2, pi/2))
|
||||
assert ParametricIntegral(C.x*C.y**4, halfcircle) == S(8192)/5
|
||||
|
||||
curve = ParametricRegion((t, t**2, t**3), (t, 0, 1))
|
||||
field1 = 8*C.x**2*C.y*C.z*C.i + 5*C.z*C.j - 4*C.x*C.y*C.k
|
||||
assert ParametricIntegral(field1, curve) == 1
|
||||
line = ParametricRegion((4*t - 1, 2 - 2*t, t), (t, 0, 1))
|
||||
assert ParametricIntegral(C.x*C.z*C.i - C.y*C.z*C.k, line) == 3
|
||||
|
||||
assert ParametricIntegral(4*C.x**3, ParametricRegion((1, t), (t, 0, 2))) == 8
|
||||
|
||||
helix = ParametricRegion((cos(t), sin(t), 3*t), (t, 0, 4*pi))
|
||||
assert ParametricIntegral(C.x*C.y*C.z, helix) == -3*sqrt(10)*pi
|
||||
|
||||
field2 = C.y*C.i + C.z*C.j + C.z*C.k
|
||||
assert ParametricIntegral(field2, ParametricRegion((cos(t), sin(t), t**2), (t, 0, pi))) == -5*pi/2 + pi**4/2
|
||||
|
||||
def test_parametric_surfaceintegrals():
|
||||
|
||||
semisphere = ParametricRegion((2*sin(phi)*cos(theta), 2*sin(phi)*sin(theta), 2*cos(phi)),\
|
||||
(theta, 0, 2*pi), (phi, 0, pi/2))
|
||||
assert ParametricIntegral(C.z, semisphere) == 8*pi
|
||||
|
||||
cylinder = ParametricRegion((sqrt(3)*cos(theta), sqrt(3)*sin(theta), z), (z, 0, 6), (theta, 0, 2*pi))
|
||||
assert ParametricIntegral(C.y, cylinder) == 0
|
||||
|
||||
cone = ParametricRegion((v*cos(u), v*sin(u), v), (u, 0, 2*pi), (v, 0, 1))
|
||||
assert ParametricIntegral(C.x*C.i + C.y*C.j + C.z**4*C.k, cone) == pi/3
|
||||
|
||||
triangle1 = ParametricRegion((x, y), (x, 0, 2), (y, 0, 10 - 5*x))
|
||||
triangle2 = ParametricRegion((x, y), (y, 0, 10 - 5*x), (x, 0, 2))
|
||||
assert ParametricIntegral(-15.6*C.y*C.k, triangle1) == ParametricIntegral(-15.6*C.y*C.k, triangle2)
|
||||
assert ParametricIntegral(C.z, triangle1) == 10*C.z
|
||||
|
||||
def test_parametric_volumeintegrals():
|
||||
|
||||
cube = ParametricRegion((x, y, z), (x, 0, 1), (y, 0, 1), (z, 0, 1))
|
||||
assert ParametricIntegral(1, cube) == 1
|
||||
|
||||
solidsphere1 = ParametricRegion((r*sin(phi)*cos(theta), r*sin(phi)*sin(theta), r*cos(phi)),\
|
||||
(r, 0, 2), (theta, 0, 2*pi), (phi, 0, pi))
|
||||
solidsphere2 = ParametricRegion((r*sin(phi)*cos(theta), r*sin(phi)*sin(theta), r*cos(phi)),\
|
||||
(r, 0, 2), (phi, 0, pi), (theta, 0, 2*pi))
|
||||
assert ParametricIntegral(C.x**2 + C.y**2, solidsphere1) == -256*pi/15
|
||||
assert ParametricIntegral(C.x**2 + C.y**2, solidsphere2) == 256*pi/15
|
||||
|
||||
region_under_plane1 = ParametricRegion((x, y, z), (x, 0, 3), (y, 0, -2*x/3 + 2),\
|
||||
(z, 0, 6 - 2*x - 3*y))
|
||||
region_under_plane2 = ParametricRegion((x, y, z), (x, 0, 3), (z, 0, 6 - 2*x - 3*y),\
|
||||
(y, 0, -2*x/3 + 2))
|
||||
|
||||
assert ParametricIntegral(C.x*C.i + C.j - 100*C.k, region_under_plane1) == \
|
||||
ParametricIntegral(C.x*C.i + C.j - 100*C.k, region_under_plane2)
|
||||
assert ParametricIntegral(2*C.x, region_under_plane2) == -9
|
||||
|
||||
def test_vector_integrate():
|
||||
halfdisc = ParametricRegion((r*cos(theta), r* sin(theta)), (r, -2, 2), (theta, 0, pi))
|
||||
assert vector_integrate(C.x**2, halfdisc) == 4*pi
|
||||
assert vector_integrate(C.x, ParametricRegion((t, t**2), (t, 2, 3))) == -17*sqrt(17)/12 + 37*sqrt(37)/12
|
||||
|
||||
assert vector_integrate(C.y**3*C.z, (C.x, 0, 3), (C.y, -1, 4)) == 765*C.z/4
|
||||
|
||||
s1 = Segment(Point(0, 0), Point(0, 1))
|
||||
assert vector_integrate(-15*C.y, s1) == S(-15)/2
|
||||
s2 = Segment(Point(4, 3, 9), Point(1, 1, 7))
|
||||
assert vector_integrate(C.y*C.i, s2) == -6
|
||||
|
||||
curve = Curve((sin(t), cos(t)), (t, 0, 2))
|
||||
assert vector_integrate(5*C.z, curve) == 10*C.z
|
||||
|
||||
c1 = Circle(Point(2, 3), 6)
|
||||
assert vector_integrate(C.x*C.y, c1) == 72*pi
|
||||
c2 = Circle(Point(0, 0), Point(1, 1), Point(1, 0))
|
||||
assert vector_integrate(1, c2) == c2.circumference
|
||||
|
||||
triangle = Polygon((0, 0), (1, 0), (1, 1))
|
||||
assert vector_integrate(C.x*C.i - 14*C.y*C.j, triangle) == 0
|
||||
p1, p2, p3, p4 = [(0, 0), (1, 0), (5, 1), (0, 1)]
|
||||
poly = Polygon(p1, p2, p3, p4)
|
||||
assert vector_integrate(-23*C.z, poly) == -161*C.z - 23*sqrt(17)*C.z
|
||||
|
||||
point = Point(2, 3)
|
||||
assert vector_integrate(C.i*C.y, point) == ParametricIntegral(C.y*C.i, ParametricRegion((2, 3)))
|
||||
|
||||
c3 = ImplicitRegion((x, y), x**2 + y**2 - 4)
|
||||
assert vector_integrate(45, c3) == 180*pi
|
||||
c4 = ImplicitRegion((x, y), (x - 3)**2 + (y - 4)**2 - 9)
|
||||
assert vector_integrate(1, c4) == 6*pi
|
||||
|
||||
pl = Plane(Point(1, 1, 1), Point(2, 3, 4), Point(2, 2, 2))
|
||||
raises(ValueError, lambda: vector_integrate(C.x*C.z*C.i + C.k, pl))
|
||||
@@ -0,0 +1,43 @@
|
||||
from sympy.vector import CoordSys3D, Gradient, Divergence, Curl, VectorZero, Laplacian
|
||||
from sympy.printing.repr import srepr
|
||||
|
||||
R = CoordSys3D('R')
|
||||
s1 = R.x*R.y*R.z # type: ignore
|
||||
s2 = R.x + 3*R.y**2 # type: ignore
|
||||
s3 = R.x**2 + R.y**2 + R.z**2 # type: ignore
|
||||
v1 = R.x*R.i + R.z*R.z*R.j # type: ignore
|
||||
v2 = R.x*R.i + R.y*R.j + R.z*R.k # type: ignore
|
||||
v3 = R.x**2*R.i + R.y**2*R.j + R.z**2*R.k # type: ignore
|
||||
|
||||
|
||||
def test_Gradient():
|
||||
assert Gradient(s1) == Gradient(R.x*R.y*R.z)
|
||||
assert Gradient(s2) == Gradient(R.x + 3*R.y**2)
|
||||
assert Gradient(s1).doit() == R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k
|
||||
assert Gradient(s2).doit() == R.i + 6*R.y*R.j
|
||||
|
||||
|
||||
def test_Divergence():
|
||||
assert Divergence(v1) == Divergence(R.x*R.i + R.z*R.z*R.j)
|
||||
assert Divergence(v2) == Divergence(R.x*R.i + R.y*R.j + R.z*R.k)
|
||||
assert Divergence(v1).doit() == 1
|
||||
assert Divergence(v2).doit() == 3
|
||||
# issue 22384
|
||||
Rc = CoordSys3D('R', transformation='cylindrical')
|
||||
assert Divergence(Rc.i).doit() == 1/Rc.r
|
||||
|
||||
|
||||
def test_Curl():
|
||||
assert Curl(v1) == Curl(R.x*R.i + R.z*R.z*R.j)
|
||||
assert Curl(v2) == Curl(R.x*R.i + R.y*R.j + R.z*R.k)
|
||||
assert Curl(v1).doit() == (-2*R.z)*R.i
|
||||
assert Curl(v2).doit() == VectorZero()
|
||||
|
||||
|
||||
def test_Laplacian():
|
||||
assert Laplacian(s3) == Laplacian(R.x**2 + R.y**2 + R.z**2)
|
||||
assert Laplacian(v3) == Laplacian(R.x**2*R.i + R.y**2*R.j + R.z**2*R.k)
|
||||
assert Laplacian(s3).doit() == 6
|
||||
assert Laplacian(v3).doit() == 2*R.i + 2*R.j + 2*R.k
|
||||
assert srepr(Laplacian(s3)) == \
|
||||
'Laplacian(Add(Pow(R.x, Integer(2)), Pow(R.y, Integer(2)), Pow(R.z, Integer(2))))'
|
||||
@@ -0,0 +1,97 @@
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.parametricregion import ParametricRegion, parametric_region_list
|
||||
from sympy.geometry import Point, Segment, Curve, Ellipse, Line, Parabola, Polygon
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.abc import a, b, r, t, x, y, z, theta, phi
|
||||
|
||||
|
||||
C = CoordSys3D('C')
|
||||
|
||||
def test_ParametricRegion():
|
||||
|
||||
point = ParametricRegion((3, 4))
|
||||
assert point.definition == (3, 4)
|
||||
assert point.parameters == ()
|
||||
assert point.limits == {}
|
||||
assert point.dimensions == 0
|
||||
|
||||
# line x = y
|
||||
line_xy = ParametricRegion((y, y), (y, 1, 5))
|
||||
assert line_xy .definition == (y, y)
|
||||
assert line_xy.parameters == (y,)
|
||||
assert line_xy.dimensions == 1
|
||||
|
||||
# line y = z
|
||||
line_yz = ParametricRegion((x,t,t), x, (t, 1, 2))
|
||||
assert line_yz.definition == (x,t,t)
|
||||
assert line_yz.parameters == (x, t)
|
||||
assert line_yz.limits == {t: (1, 2)}
|
||||
assert line_yz.dimensions == 1
|
||||
|
||||
p1 = ParametricRegion((9*a, -16*b), (a, 0, 2), (b, -1, 5))
|
||||
assert p1.definition == (9*a, -16*b)
|
||||
assert p1.parameters == (a, b)
|
||||
assert p1.limits == {a: (0, 2), b: (-1, 5)}
|
||||
assert p1.dimensions == 2
|
||||
|
||||
p2 = ParametricRegion((t, t**3), t)
|
||||
assert p2.parameters == (t,)
|
||||
assert p2.limits == {}
|
||||
assert p2.dimensions == 0
|
||||
|
||||
circle = ParametricRegion((r*cos(theta), r*sin(theta)), r, (theta, 0, 2*pi))
|
||||
assert circle.definition == (r*cos(theta), r*sin(theta))
|
||||
assert circle.dimensions == 1
|
||||
|
||||
halfdisc = ParametricRegion((r*cos(theta), r*sin(theta)), (r, -2, 2), (theta, 0, pi))
|
||||
assert halfdisc.definition == (r*cos(theta), r*sin(theta))
|
||||
assert halfdisc.parameters == (r, theta)
|
||||
assert halfdisc.limits == {r: (-2, 2), theta: (0, pi)}
|
||||
assert halfdisc.dimensions == 2
|
||||
|
||||
ellipse = ParametricRegion((a*cos(t), b*sin(t)), (t, 0, 8))
|
||||
assert ellipse.parameters == (t,)
|
||||
assert ellipse.limits == {t: (0, 8)}
|
||||
assert ellipse.dimensions == 1
|
||||
|
||||
cylinder = ParametricRegion((r*cos(theta), r*sin(theta), z), (r, 0, 1), (theta, 0, 2*pi), (z, 0, 4))
|
||||
assert cylinder.parameters == (r, theta, z)
|
||||
assert cylinder.dimensions == 3
|
||||
|
||||
sphere = ParametricRegion((r*sin(phi)*cos(theta),r*sin(phi)*sin(theta), r*cos(phi)),
|
||||
r, (theta, 0, 2*pi), (phi, 0, pi))
|
||||
assert sphere.definition == (r*sin(phi)*cos(theta),r*sin(phi)*sin(theta), r*cos(phi))
|
||||
assert sphere.parameters == (r, theta, phi)
|
||||
assert sphere.dimensions == 2
|
||||
|
||||
raises(ValueError, lambda: ParametricRegion((a*t**2, 2*a*t), (a, -2)))
|
||||
raises(ValueError, lambda: ParametricRegion((a, b), (a**2, sin(b)), (a, 2, 4, 6)))
|
||||
|
||||
|
||||
def test_parametric_region_list():
|
||||
|
||||
point = Point(-5, 12)
|
||||
assert parametric_region_list(point) == [ParametricRegion((-5, 12))]
|
||||
|
||||
e = Ellipse(Point(2, 8), 2, 6)
|
||||
assert parametric_region_list(e, t) == [ParametricRegion((2*cos(t) + 2, 6*sin(t) + 8), (t, 0, 2*pi))]
|
||||
|
||||
c = Curve((t, t**3), (t, 5, 3))
|
||||
assert parametric_region_list(c) == [ParametricRegion((t, t**3), (t, 5, 3))]
|
||||
|
||||
s = Segment(Point(2, 11, -6), Point(0, 2, 5))
|
||||
assert parametric_region_list(s, t) == [ParametricRegion((2 - 2*t, 11 - 9*t, 11*t - 6), (t, 0, 1))]
|
||||
s1 = Segment(Point(0, 0), (1, 0))
|
||||
assert parametric_region_list(s1, t) == [ParametricRegion((t, 0), (t, 0, 1))]
|
||||
s2 = Segment(Point(1, 2, 3), Point(1, 2, 5))
|
||||
assert parametric_region_list(s2, t) == [ParametricRegion((1, 2, 2*t + 3), (t, 0, 1))]
|
||||
s3 = Segment(Point(12, 56), Point(12, 56))
|
||||
assert parametric_region_list(s3) == [ParametricRegion((12, 56))]
|
||||
|
||||
poly = Polygon((1,3), (-3, 8), (2, 4))
|
||||
assert parametric_region_list(poly, t) == [ParametricRegion((1 - 4*t, 5*t + 3), (t, 0, 1)), ParametricRegion((5*t - 3, 8 - 4*t), (t, 0, 1)), ParametricRegion((2 - t, 4 - t), (t, 0, 1))]
|
||||
|
||||
p1 = Parabola(Point(0, 0), Line(Point(5, 8), Point(7,8)))
|
||||
raises(ValueError, lambda: parametric_region_list(p1))
|
||||
@@ -0,0 +1,221 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from sympy.core.function import Function
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.printing.latex import latex
|
||||
from sympy.printing.pretty import pretty as xpretty
|
||||
from sympy.vector import CoordSys3D, Del, Vector, express
|
||||
from sympy.abc import a, b, c
|
||||
from sympy.testing.pytest import XFAIL
|
||||
|
||||
|
||||
def pretty(expr):
|
||||
"""ASCII pretty-printing"""
|
||||
return xpretty(expr, use_unicode=False, wrap_line=False)
|
||||
|
||||
|
||||
def upretty(expr):
|
||||
"""Unicode pretty-printing"""
|
||||
return xpretty(expr, use_unicode=True, wrap_line=False)
|
||||
|
||||
|
||||
# Initialize the basic and tedious vector/dyadic expressions
|
||||
# needed for testing.
|
||||
# Some of the pretty forms shown denote how the expressions just
|
||||
# above them should look with pretty printing.
|
||||
N = CoordSys3D('N')
|
||||
C = N.orient_new_axis('C', a, N.k) # type: ignore
|
||||
v = []
|
||||
d = []
|
||||
v.append(Vector.zero)
|
||||
v.append(N.i) # type: ignore
|
||||
v.append(-N.i) # type: ignore
|
||||
v.append(N.i + N.j) # type: ignore
|
||||
v.append(a*N.i) # type: ignore
|
||||
v.append(a*N.i - b*N.j) # type: ignore
|
||||
v.append((a**2 + N.x)*N.i + N.k) # type: ignore
|
||||
v.append((a**2 + b)*N.i + 3*(C.y - c)*N.k) # type: ignore
|
||||
f = Function('f')
|
||||
v.append(N.j - (Integral(f(b)) - C.x**2)*N.k) # type: ignore
|
||||
upretty_v_8 = """\
|
||||
⎛ 2 ⌠ ⎞ \n\
|
||||
j_N + ⎜x_C - ⎮ f(b) db⎟ k_N\n\
|
||||
⎝ ⌡ ⎠ \
|
||||
"""
|
||||
pretty_v_8 = """\
|
||||
j_N + / / \\\n\
|
||||
| 2 | |\n\
|
||||
|x_C - | f(b) db|\n\
|
||||
| | |\n\
|
||||
\\ / / \
|
||||
"""
|
||||
|
||||
v.append(N.i + C.k) # type: ignore
|
||||
v.append(express(N.i, C)) # type: ignore
|
||||
v.append((a**2 + b)*N.i + (Integral(f(b)))*N.k) # type: ignore
|
||||
upretty_v_11 = """\
|
||||
⎛ 2 ⎞ ⎛⌠ ⎞ \n\
|
||||
⎝a + b⎠ i_N + ⎜⎮ f(b) db⎟ k_N\n\
|
||||
⎝⌡ ⎠ \
|
||||
"""
|
||||
pretty_v_11 = """\
|
||||
/ 2 \\ + / / \\\n\
|
||||
\\a + b/ i_N| | |\n\
|
||||
| | f(b) db|\n\
|
||||
| | |\n\
|
||||
\\/ / \
|
||||
"""
|
||||
|
||||
for x in v:
|
||||
d.append(x | N.k) # type: ignore
|
||||
s = 3*N.x**2*C.y # type: ignore
|
||||
upretty_s = """\
|
||||
2\n\
|
||||
3⋅y_C⋅x_N \
|
||||
"""
|
||||
pretty_s = """\
|
||||
2\n\
|
||||
3*y_C*x_N \
|
||||
"""
|
||||
|
||||
# This is the pretty form for ((a**2 + b)*N.i + 3*(C.y - c)*N.k) | N.k
|
||||
upretty_d_7 = """\
|
||||
⎛ 2 ⎞ \n\
|
||||
⎝a + b⎠ (i_N|k_N) + (3⋅y_C - 3⋅c) (k_N|k_N)\
|
||||
"""
|
||||
pretty_d_7 = """\
|
||||
/ 2 \\ (i_N|k_N) + (3*y_C - 3*c) (k_N|k_N)\n\
|
||||
\\a + b/ \
|
||||
"""
|
||||
|
||||
|
||||
def test_str_printing():
|
||||
assert str(v[0]) == '0'
|
||||
assert str(v[1]) == 'N.i'
|
||||
assert str(v[2]) == '(-1)*N.i'
|
||||
assert str(v[3]) == 'N.i + N.j'
|
||||
assert str(v[8]) == 'N.j + (C.x**2 - Integral(f(b), b))*N.k'
|
||||
assert str(v[9]) == 'C.k + N.i'
|
||||
assert str(s) == '3*C.y*N.x**2'
|
||||
assert str(d[0]) == '0'
|
||||
assert str(d[1]) == '(N.i|N.k)'
|
||||
assert str(d[4]) == 'a*(N.i|N.k)'
|
||||
assert str(d[5]) == 'a*(N.i|N.k) + (-b)*(N.j|N.k)'
|
||||
assert str(d[8]) == ('(N.j|N.k) + (C.x**2 - ' +
|
||||
'Integral(f(b), b))*(N.k|N.k)')
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_pretty_printing_ascii():
|
||||
assert pretty(v[0]) == '0'
|
||||
assert pretty(v[1]) == 'i_N'
|
||||
assert pretty(v[5]) == '(a) i_N + (-b) j_N'
|
||||
assert pretty(v[8]) == pretty_v_8
|
||||
assert pretty(v[2]) == '(-1) i_N'
|
||||
assert pretty(v[11]) == pretty_v_11
|
||||
assert pretty(s) == pretty_s
|
||||
assert pretty(d[0]) == '(0|0)'
|
||||
assert pretty(d[5]) == '(a) (i_N|k_N) + (-b) (j_N|k_N)'
|
||||
assert pretty(d[7]) == pretty_d_7
|
||||
assert pretty(d[10]) == '(cos(a)) (i_C|k_N) + (-sin(a)) (j_C|k_N)'
|
||||
|
||||
|
||||
def test_pretty_print_unicode_v():
|
||||
assert upretty(v[0]) == '0'
|
||||
assert upretty(v[1]) == 'i_N'
|
||||
assert upretty(v[5]) == '(a) i_N + (-b) j_N'
|
||||
# Make sure the printing works in other objects
|
||||
assert upretty(v[5].args) == '((a) i_N, (-b) j_N)'
|
||||
assert upretty(v[8]) == upretty_v_8
|
||||
assert upretty(v[2]) == '(-1) i_N'
|
||||
assert upretty(v[11]) == upretty_v_11
|
||||
assert upretty(s) == upretty_s
|
||||
assert upretty(d[0]) == '(0|0)'
|
||||
assert upretty(d[5]) == '(a) (i_N|k_N) + (-b) (j_N|k_N)'
|
||||
assert upretty(d[7]) == upretty_d_7
|
||||
assert upretty(d[10]) == '(cos(a)) (i_C|k_N) + (-sin(a)) (j_C|k_N)'
|
||||
|
||||
|
||||
def test_latex_printing():
|
||||
assert latex(v[0]) == '\\mathbf{\\hat{0}}'
|
||||
assert latex(v[1]) == '\\mathbf{\\hat{i}_{N}}'
|
||||
assert latex(v[2]) == '- \\mathbf{\\hat{i}_{N}}'
|
||||
assert latex(v[5]) == ('\\left(a\\right)\\mathbf{\\hat{i}_{N}} + ' +
|
||||
'\\left(- b\\right)\\mathbf{\\hat{j}_{N}}')
|
||||
assert latex(v[6]) == ('\\left(\\mathbf{{x}_{N}} + a^{2}\\right)\\mathbf{\\hat{i}_' +
|
||||
'{N}} + \\mathbf{\\hat{k}_{N}}')
|
||||
assert latex(v[8]) == ('\\mathbf{\\hat{j}_{N}} + \\left(\\mathbf{{x}_' +
|
||||
'{C}}^{2} - \\int f{\\left(b \\right)}\\,' +
|
||||
' db\\right)\\mathbf{\\hat{k}_{N}}')
|
||||
assert latex(s) == '3 \\mathbf{{y}_{C}} \\mathbf{{x}_{N}}^{2}'
|
||||
assert latex(d[0]) == '(\\mathbf{\\hat{0}}|\\mathbf{\\hat{0}})'
|
||||
assert latex(d[4]) == ('\\left(a\\right)\\left(\\mathbf{\\hat{i}_{N}}{\\middle|}' +
|
||||
'\\mathbf{\\hat{k}_{N}}\\right)')
|
||||
assert latex(d[9]) == ('\\left(\\mathbf{\\hat{k}_{C}}{\\middle|}' +
|
||||
'\\mathbf{\\hat{k}_{N}}\\right) + \\left(' +
|
||||
'\\mathbf{\\hat{i}_{N}}{\\middle|}\\mathbf{' +
|
||||
'\\hat{k}_{N}}\\right)')
|
||||
assert latex(d[11]) == ('\\left(a^{2} + b\\right)\\left(\\mathbf{\\hat{i}_{N}}' +
|
||||
'{\\middle|}\\mathbf{\\hat{k}_{N}}\\right) + ' +
|
||||
'\\left(\\int f{\\left(b \\right)}\\, db\\right)\\left(' +
|
||||
'\\mathbf{\\hat{k}_{N}}{\\middle|}\\mathbf{' +
|
||||
'\\hat{k}_{N}}\\right)')
|
||||
|
||||
def test_issue_23058():
|
||||
from sympy import symbols, sin, cos, pi, UnevaluatedExpr
|
||||
|
||||
delop = Del()
|
||||
CC_ = CoordSys3D("C")
|
||||
y = CC_.y
|
||||
xhat = CC_.i
|
||||
|
||||
t = symbols("t")
|
||||
ten = symbols("10", positive=True)
|
||||
eps, mu = 4*pi*ten**(-11), ten**(-5)
|
||||
|
||||
Bx = 2 * ten**(-4) * cos(ten**5 * t) * sin(ten**(-3) * y)
|
||||
vecB = Bx * xhat
|
||||
vecE = (1/eps) * Integral(delop.cross(vecB/mu).doit(), t)
|
||||
vecE = vecE.doit()
|
||||
|
||||
vecB_str = """\
|
||||
⎛ ⎛y_C⎞ ⎛ 5 ⎞⎞ \n\
|
||||
⎜2⋅sin⎜───⎟⋅cos⎝10 ⋅t⎠⎟ i_C\n\
|
||||
⎜ ⎜ 3⎟ ⎟ \n\
|
||||
⎜ ⎝10 ⎠ ⎟ \n\
|
||||
⎜─────────────────────⎟ \n\
|
||||
⎜ 4 ⎟ \n\
|
||||
⎝ 10 ⎠ \
|
||||
"""
|
||||
vecE_str = """\
|
||||
⎛ 4 ⎛ 5 ⎞ ⎛y_C⎞ ⎞ \n\
|
||||
⎜-10 ⋅sin⎝10 ⋅t⎠⋅cos⎜───⎟ ⎟ k_C\n\
|
||||
⎜ ⎜ 3⎟ ⎟ \n\
|
||||
⎜ ⎝10 ⎠ ⎟ \n\
|
||||
⎜─────────────────────────⎟ \n\
|
||||
⎝ 2⋅π ⎠ \
|
||||
"""
|
||||
|
||||
assert upretty(vecB) == vecB_str
|
||||
assert upretty(vecE) == vecE_str
|
||||
|
||||
ten = UnevaluatedExpr(10)
|
||||
eps, mu = 4*pi*ten**(-11), ten**(-5)
|
||||
|
||||
Bx = 2 * ten**(-4) * cos(ten**5 * t) * sin(ten**(-3) * y)
|
||||
vecB = Bx * xhat
|
||||
|
||||
vecB_str = """\
|
||||
⎛ -4 ⎛ 5⎞ ⎛ -3⎞⎞ \n\
|
||||
⎝2⋅10 ⋅cos⎝t⋅10 ⎠⋅sin⎝y_C⋅10 ⎠⎠ i_C \
|
||||
"""
|
||||
assert upretty(vecB) == vecB_str
|
||||
|
||||
def test_custom_names():
|
||||
A = CoordSys3D('A', vector_names=['x', 'y', 'z'],
|
||||
variable_names=['i', 'j', 'k'])
|
||||
assert A.i.__str__() == 'A.i'
|
||||
assert A.x.__str__() == 'A.x'
|
||||
assert A.i._pretty_form == 'i_A'
|
||||
assert A.x._pretty_form == 'x_A'
|
||||
assert A.i._latex_form == r'\mathbf{{i}_{A}}'
|
||||
assert A.x._latex_form == r"\mathbf{\hat{x}_{A}}"
|
||||
@@ -0,0 +1,342 @@
|
||||
from sympy.core import Rational, S, Add, Mul, I
|
||||
from sympy.simplify import simplify, trigsimp
|
||||
from sympy.core.function import (Derivative, Function, diff)
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
||||
from sympy.vector.vector import Vector, BaseVector, VectorAdd, \
|
||||
VectorMul, VectorZero
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.vector import Cross, Dot, cross
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.vector.kind import VectorKind
|
||||
from sympy.core.kind import NumberKind
|
||||
from sympy.testing.pytest import XFAIL
|
||||
|
||||
|
||||
C = CoordSys3D('C')
|
||||
|
||||
i, j, k = C.base_vectors()
|
||||
a, b, c = symbols('a b c')
|
||||
|
||||
|
||||
def test_cross():
|
||||
v1 = C.x * i + C.z * C.z * j
|
||||
v2 = C.x * i + C.y * j + C.z * k
|
||||
assert Cross(v1, v2) == Cross(C.x*C.i + C.z**2*C.j, C.x*C.i + C.y*C.j + C.z*C.k)
|
||||
assert Cross(v1, v2).doit() == C.z**3*C.i + (-C.x*C.z)*C.j + (C.x*C.y - C.x*C.z**2)*C.k
|
||||
assert cross(v1, v2) == C.z**3*C.i + (-C.x*C.z)*C.j + (C.x*C.y - C.x*C.z**2)*C.k
|
||||
assert Cross(v1, v2) == -Cross(v2, v1)
|
||||
# XXX: Cannot use Cross here. See XFAIL test below:
|
||||
assert cross(v1, v2) + cross(v2, v1) == Vector.zero
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_cross_xfail():
|
||||
v1 = C.x * i + C.z * C.z * j
|
||||
v2 = C.x * i + C.y * j + C.z * k
|
||||
assert Cross(v1, v2) + Cross(v2, v1) == Vector.zero
|
||||
|
||||
|
||||
def test_dot():
|
||||
v1 = C.x * i + C.z * C.z * j
|
||||
v2 = C.x * i + C.y * j + C.z * k
|
||||
assert Dot(v1, v2) == Dot(C.x*C.i + C.z**2*C.j, C.x*C.i + C.y*C.j + C.z*C.k)
|
||||
assert Dot(v1, v2).doit() == C.x**2 + C.y*C.z**2
|
||||
assert Dot(v2, v1).doit() == C.x**2 + C.y*C.z**2
|
||||
assert Dot(v1, v2) == Dot(v2, v1)
|
||||
|
||||
|
||||
def test_vector_sympy():
|
||||
"""
|
||||
Test whether the Vector framework confirms to the hashing
|
||||
and equality testing properties of SymPy.
|
||||
"""
|
||||
v1 = 3*j
|
||||
assert v1 == j*3
|
||||
assert v1.components == {j: 3}
|
||||
v2 = 3*i + 4*j + 5*k
|
||||
v3 = 2*i + 4*j + i + 4*k + k
|
||||
assert v3 == v2
|
||||
assert v3.__hash__() == v2.__hash__()
|
||||
|
||||
|
||||
def test_kind():
|
||||
assert C.i.kind is VectorKind(NumberKind)
|
||||
assert C.j.kind is VectorKind(NumberKind)
|
||||
assert C.k.kind is VectorKind(NumberKind)
|
||||
|
||||
assert C.x.kind is NumberKind
|
||||
assert C.y.kind is NumberKind
|
||||
assert C.z.kind is NumberKind
|
||||
|
||||
assert Mul._kind_dispatcher(NumberKind, VectorKind(NumberKind)) is VectorKind(NumberKind)
|
||||
assert Mul(2, C.i).kind is VectorKind(NumberKind)
|
||||
|
||||
v1 = C.x * i + C.z * C.z * j
|
||||
v2 = C.x * i + C.y * j + C.z * k
|
||||
assert v1.kind is VectorKind(NumberKind)
|
||||
assert v2.kind is VectorKind(NumberKind)
|
||||
|
||||
assert (v1 + v2).kind is VectorKind(NumberKind)
|
||||
assert Add(v1, v2).kind is VectorKind(NumberKind)
|
||||
assert Cross(v1, v2).doit().kind is VectorKind(NumberKind)
|
||||
assert VectorAdd(v1, v2).kind is VectorKind(NumberKind)
|
||||
assert VectorMul(2, v1).kind is VectorKind(NumberKind)
|
||||
assert VectorZero().kind is VectorKind(NumberKind)
|
||||
|
||||
assert v1.projection(v2).kind is VectorKind(NumberKind)
|
||||
assert v2.projection(v1).kind is VectorKind(NumberKind)
|
||||
|
||||
|
||||
def test_vectoradd():
|
||||
assert isinstance(Add(C.i, C.j), VectorAdd)
|
||||
v1 = C.x * i + C.z * C.z * j
|
||||
v2 = C.x * i + C.y * j + C.z * k
|
||||
assert isinstance(Add(v1, v2), VectorAdd)
|
||||
|
||||
# https://github.com/sympy/sympy/issues/26121
|
||||
|
||||
E = Matrix([C.i, C.j, C.k]).T
|
||||
a = Matrix([1, 2, 3])
|
||||
av = E*a
|
||||
|
||||
assert av[0].kind == VectorKind()
|
||||
assert isinstance(av[0], VectorAdd)
|
||||
|
||||
|
||||
def test_vector():
|
||||
assert isinstance(i, BaseVector)
|
||||
assert i != j
|
||||
assert j != k
|
||||
assert k != i
|
||||
assert i - i == Vector.zero
|
||||
assert i + Vector.zero == i
|
||||
assert i - Vector.zero == i
|
||||
assert Vector.zero != 0
|
||||
assert -Vector.zero == Vector.zero
|
||||
|
||||
v1 = a*i + b*j + c*k
|
||||
v2 = a**2*i + b**2*j + c**2*k
|
||||
v3 = v1 + v2
|
||||
v4 = 2 * v1
|
||||
v5 = a * i
|
||||
|
||||
assert isinstance(v1, VectorAdd)
|
||||
assert v1 - v1 == Vector.zero
|
||||
assert v1 + Vector.zero == v1
|
||||
assert v1.dot(i) == a
|
||||
assert v1.dot(j) == b
|
||||
assert v1.dot(k) == c
|
||||
assert i.dot(v2) == a**2
|
||||
assert j.dot(v2) == b**2
|
||||
assert k.dot(v2) == c**2
|
||||
assert v3.dot(i) == a**2 + a
|
||||
assert v3.dot(j) == b**2 + b
|
||||
assert v3.dot(k) == c**2 + c
|
||||
|
||||
assert v1 + v2 == v2 + v1
|
||||
assert v1 - v2 == -1 * (v2 - v1)
|
||||
assert a * v1 == v1 * a
|
||||
|
||||
assert isinstance(v5, VectorMul)
|
||||
assert v5.base_vector == i
|
||||
assert v5.measure_number == a
|
||||
assert isinstance(v4, Vector)
|
||||
assert isinstance(v4, VectorAdd)
|
||||
assert isinstance(v4, Vector)
|
||||
assert isinstance(Vector.zero, VectorZero)
|
||||
assert isinstance(Vector.zero, Vector)
|
||||
assert isinstance(v1 * 0, VectorZero)
|
||||
|
||||
assert v1.to_matrix(C) == Matrix([[a], [b], [c]])
|
||||
|
||||
assert i.components == {i: 1}
|
||||
assert v5.components == {i: a}
|
||||
assert v1.components == {i: a, j: b, k: c}
|
||||
|
||||
assert VectorAdd(v1, Vector.zero) == v1
|
||||
assert VectorMul(a, v1) == v1*a
|
||||
assert VectorMul(1, i) == i
|
||||
assert VectorAdd(v1, Vector.zero) == v1
|
||||
assert VectorMul(0, Vector.zero) == Vector.zero
|
||||
raises(TypeError, lambda: v1.outer(1))
|
||||
raises(TypeError, lambda: v1.dot(1))
|
||||
|
||||
|
||||
def test_vector_magnitude_normalize():
|
||||
assert Vector.zero.magnitude() == 0
|
||||
assert Vector.zero.normalize() == Vector.zero
|
||||
|
||||
assert i.magnitude() == 1
|
||||
assert j.magnitude() == 1
|
||||
assert k.magnitude() == 1
|
||||
assert i.normalize() == i
|
||||
assert j.normalize() == j
|
||||
assert k.normalize() == k
|
||||
|
||||
v1 = a * i
|
||||
assert v1.normalize() == (a/sqrt(a**2))*i
|
||||
assert v1.magnitude() == sqrt(a**2)
|
||||
|
||||
v2 = a*i + b*j + c*k
|
||||
assert v2.magnitude() == sqrt(a**2 + b**2 + c**2)
|
||||
assert v2.normalize() == v2 / v2.magnitude()
|
||||
|
||||
v3 = i + j
|
||||
assert v3.normalize() == (sqrt(2)/2)*C.i + (sqrt(2)/2)*C.j
|
||||
|
||||
|
||||
def test_vector_simplify():
|
||||
A, s, k, m = symbols('A, s, k, m')
|
||||
|
||||
test1 = (1 / a + 1 / b) * i
|
||||
assert (test1 & i) != (a + b) / (a * b)
|
||||
test1 = simplify(test1)
|
||||
assert (test1 & i) == (a + b) / (a * b)
|
||||
assert test1.simplify() == simplify(test1)
|
||||
|
||||
test2 = (A**2 * s**4 / (4 * pi * k * m**3)) * i
|
||||
test2 = simplify(test2)
|
||||
assert (test2 & i) == (A**2 * s**4 / (4 * pi * k * m**3))
|
||||
|
||||
test3 = ((4 + 4 * a - 2 * (2 + 2 * a)) / (2 + 2 * a)) * i
|
||||
test3 = simplify(test3)
|
||||
assert (test3 & i) == 0
|
||||
|
||||
test4 = ((-4 * a * b**2 - 2 * b**3 - 2 * a**2 * b) / (a + b)**2) * i
|
||||
test4 = simplify(test4)
|
||||
assert (test4 & i) == -2 * b
|
||||
|
||||
v = (sin(a)+cos(a))**2*i - j
|
||||
assert trigsimp(v) == (2*sin(a + pi/4)**2)*i + (-1)*j
|
||||
assert trigsimp(v) == v.trigsimp()
|
||||
|
||||
assert simplify(Vector.zero) == Vector.zero
|
||||
|
||||
|
||||
def test_vector_equals():
|
||||
assert (2*i).equals(j) is False
|
||||
assert i.equals(i) is True
|
||||
|
||||
# https://github.com/sympy/sympy/issues/25915
|
||||
A = (sqrt(2) + sqrt(6)) / sqrt(sqrt(3) + 2)
|
||||
assert (A*i).equals(2*i) is True
|
||||
assert (A*i).equals(3*i) is False
|
||||
|
||||
# Test comparing vectors in different coordinate systems
|
||||
D = C.orient_new_axis('D', pi/2, C.k)
|
||||
assert (D.i).equals(C.j) is True
|
||||
assert (D.i).equals(C.i) is False
|
||||
|
||||
|
||||
def test_vector_conjugate():
|
||||
# https://github.com/sympy/sympy/issues/27094
|
||||
assert (I*i + (1 + I)*j + 2*k).conjugate() == -I*i + (1 - I)*j + 2*k
|
||||
|
||||
|
||||
def test_vector_dot():
|
||||
assert i.dot(Vector.zero) == 0
|
||||
assert Vector.zero.dot(i) == 0
|
||||
assert i & Vector.zero == 0
|
||||
|
||||
assert i.dot(i) == 1
|
||||
assert i.dot(j) == 0
|
||||
assert i.dot(k) == 0
|
||||
assert i & i == 1
|
||||
assert i & j == 0
|
||||
assert i & k == 0
|
||||
|
||||
assert j.dot(i) == 0
|
||||
assert j.dot(j) == 1
|
||||
assert j.dot(k) == 0
|
||||
assert j & i == 0
|
||||
assert j & j == 1
|
||||
assert j & k == 0
|
||||
|
||||
assert k.dot(i) == 0
|
||||
assert k.dot(j) == 0
|
||||
assert k.dot(k) == 1
|
||||
assert k & i == 0
|
||||
assert k & j == 0
|
||||
assert k & k == 1
|
||||
|
||||
raises(TypeError, lambda: k.dot(1))
|
||||
|
||||
|
||||
def test_vector_cross():
|
||||
assert i.cross(Vector.zero) == Vector.zero
|
||||
assert Vector.zero.cross(i) == Vector.zero
|
||||
|
||||
assert i.cross(i) == Vector.zero
|
||||
assert i.cross(j) == k
|
||||
assert i.cross(k) == -j
|
||||
assert i ^ i == Vector.zero
|
||||
assert i ^ j == k
|
||||
assert i ^ k == -j
|
||||
|
||||
assert j.cross(i) == -k
|
||||
assert j.cross(j) == Vector.zero
|
||||
assert j.cross(k) == i
|
||||
assert j ^ i == -k
|
||||
assert j ^ j == Vector.zero
|
||||
assert j ^ k == i
|
||||
|
||||
assert k.cross(i) == j
|
||||
assert k.cross(j) == -i
|
||||
assert k.cross(k) == Vector.zero
|
||||
assert k ^ i == j
|
||||
assert k ^ j == -i
|
||||
assert k ^ k == Vector.zero
|
||||
|
||||
assert k.cross(1) == Cross(k, 1)
|
||||
|
||||
|
||||
def test_projection():
|
||||
v1 = i + j + k
|
||||
v2 = 3*i + 4*j
|
||||
v3 = 0*i + 0*j
|
||||
assert v1.projection(v1) == i + j + k
|
||||
assert v1.projection(v2) == Rational(7, 3)*C.i + Rational(7, 3)*C.j + Rational(7, 3)*C.k
|
||||
assert v1.projection(v1, scalar=True) == S.One
|
||||
assert v1.projection(v2, scalar=True) == Rational(7, 3)
|
||||
assert v3.projection(v1) == Vector.zero
|
||||
assert v3.projection(v1, scalar=True) == S.Zero
|
||||
|
||||
|
||||
def test_vector_diff_integrate():
|
||||
f = Function('f')
|
||||
v = f(a)*C.i + a**2*C.j - C.k
|
||||
assert Derivative(v, a) == Derivative((f(a))*C.i +
|
||||
a**2*C.j + (-1)*C.k, a)
|
||||
assert (diff(v, a) == v.diff(a) == Derivative(v, a).doit() ==
|
||||
(Derivative(f(a), a))*C.i + 2*a*C.j)
|
||||
assert (Integral(v, a) == (Integral(f(a), a))*C.i +
|
||||
(Integral(a**2, a))*C.j + (Integral(-1, a))*C.k)
|
||||
|
||||
|
||||
def test_vector_args():
|
||||
raises(ValueError, lambda: BaseVector(3, C))
|
||||
raises(TypeError, lambda: BaseVector(0, Vector.zero))
|
||||
|
||||
|
||||
def test_srepr():
|
||||
from sympy.printing.repr import srepr
|
||||
res = "CoordSys3D(Str('C'), Tuple(ImmutableDenseMatrix([[Integer(1), "\
|
||||
"Integer(0), Integer(0)], [Integer(0), Integer(1), Integer(0)], "\
|
||||
"[Integer(0), Integer(0), Integer(1)]]), VectorZero())).i"
|
||||
assert srepr(C.i) == res
|
||||
|
||||
|
||||
def test_scalar():
|
||||
from sympy.vector import CoordSys3D
|
||||
C = CoordSys3D('C')
|
||||
v1 = 3*C.i + 4*C.j + 5*C.k
|
||||
v2 = 3*C.i - 4*C.j + 5*C.k
|
||||
assert v1.is_Vector is True
|
||||
assert v1.is_scalar is False
|
||||
assert (v1.dot(v2)).is_scalar is True
|
||||
assert (v1.cross(v2)).is_scalar is False
|
||||
@@ -0,0 +1,714 @@
|
||||
from __future__ import annotations
|
||||
from itertools import product
|
||||
|
||||
from sympy.core import Add, Basic
|
||||
from sympy.core.assumptions import StdFactKB
|
||||
from sympy.core.expr import AtomicExpr, Expr
|
||||
from sympy.core.power import Pow
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.sorting import default_sort_key
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix
|
||||
from sympy.vector.basisdependent import (BasisDependentZero,
|
||||
BasisDependent, BasisDependentMul, BasisDependentAdd)
|
||||
from sympy.vector.coordsysrect import CoordSys3D
|
||||
from sympy.vector.dyadic import Dyadic, BaseDyadic, DyadicAdd
|
||||
from sympy.vector.kind import VectorKind
|
||||
|
||||
|
||||
class Vector(BasisDependent):
|
||||
"""
|
||||
Super class for all Vector classes.
|
||||
Ideally, neither this class nor any of its subclasses should be
|
||||
instantiated by the user.
|
||||
"""
|
||||
|
||||
is_scalar = False
|
||||
is_Vector = True
|
||||
_op_priority = 12.0
|
||||
|
||||
_expr_type: type[Vector]
|
||||
_mul_func: type[Vector]
|
||||
_add_func: type[Vector]
|
||||
_zero_func: type[Vector]
|
||||
_base_func: type[Vector]
|
||||
zero: VectorZero
|
||||
|
||||
kind: VectorKind = VectorKind()
|
||||
|
||||
@property
|
||||
def components(self):
|
||||
"""
|
||||
Returns the components of this vector in the form of a
|
||||
Python dictionary mapping BaseVector instances to the
|
||||
corresponding measure numbers.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> v = 3*C.i + 4*C.j + 5*C.k
|
||||
>>> v.components
|
||||
{C.i: 3, C.j: 4, C.k: 5}
|
||||
|
||||
"""
|
||||
# The '_components' attribute is defined according to the
|
||||
# subclass of Vector the instance belongs to.
|
||||
return self._components
|
||||
|
||||
def magnitude(self):
|
||||
"""
|
||||
Returns the magnitude of this vector.
|
||||
"""
|
||||
return sqrt(self & self)
|
||||
|
||||
def normalize(self):
|
||||
"""
|
||||
Returns the normalized version of this vector.
|
||||
"""
|
||||
return self / self.magnitude()
|
||||
|
||||
def equals(self, other):
|
||||
"""
|
||||
Check if ``self`` and ``other`` are identically equal vectors.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Checks if two vector expressions are equal for all possible values of
|
||||
the symbols present in the expressions.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy.abc import x, y
|
||||
>>> from sympy import pi
|
||||
>>> C = CoordSys3D('C')
|
||||
|
||||
Compare vectors that are equal or not:
|
||||
|
||||
>>> C.i.equals(C.j)
|
||||
False
|
||||
>>> C.i.equals(C.i)
|
||||
True
|
||||
|
||||
These two vectors are equal if `x = y` but are not identically equal
|
||||
as expressions since for some values of `x` and `y` they are unequal:
|
||||
|
||||
>>> v1 = x*C.i + C.j
|
||||
>>> v2 = y*C.i + C.j
|
||||
>>> v1.equals(v1)
|
||||
True
|
||||
>>> v1.equals(v2)
|
||||
False
|
||||
|
||||
Vectors from different coordinate systems can be compared:
|
||||
|
||||
>>> D = C.orient_new_axis('D', pi/2, C.i)
|
||||
>>> D.j.equals(C.j)
|
||||
False
|
||||
>>> D.j.equals(C.k)
|
||||
True
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other: Vector
|
||||
The other vector expression to compare with.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
``True``, ``False`` or ``None``. A return value of ``True`` indicates
|
||||
that the two vectors are identically equal. A return value of ``False``
|
||||
indicates that they are not. In some cases it is not possible to
|
||||
determine if the two vectors are identically equal and ``None`` is
|
||||
returned.
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.core.expr.Expr.equals
|
||||
"""
|
||||
diff = self - other
|
||||
diff_mag2 = diff.dot(diff)
|
||||
return diff_mag2.equals(0)
|
||||
|
||||
def dot(self, other):
|
||||
"""
|
||||
Returns the dot product of this Vector, either with another
|
||||
Vector, or a Dyadic, or a Del operator.
|
||||
If 'other' is a Vector, returns the dot product scalar (SymPy
|
||||
expression).
|
||||
If 'other' is a Dyadic, the dot product is returned as a Vector.
|
||||
If 'other' is an instance of Del, returns the directional
|
||||
derivative operator as a Python function. If this function is
|
||||
applied to a scalar expression, it returns the directional
|
||||
derivative of the scalar field wrt this Vector.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other: Vector/Dyadic/Del
|
||||
The Vector or Dyadic we are dotting with, or a Del operator .
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Del
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> delop = Del()
|
||||
>>> C.i.dot(C.j)
|
||||
0
|
||||
>>> C.i & C.i
|
||||
1
|
||||
>>> v = 3*C.i + 4*C.j + 5*C.k
|
||||
>>> v.dot(C.k)
|
||||
5
|
||||
>>> (C.i & delop)(C.x*C.y*C.z)
|
||||
C.y*C.z
|
||||
>>> d = C.i.outer(C.i)
|
||||
>>> C.i.dot(d)
|
||||
C.i
|
||||
|
||||
"""
|
||||
|
||||
# Check special cases
|
||||
if isinstance(other, Dyadic):
|
||||
if isinstance(self, VectorZero):
|
||||
return Vector.zero
|
||||
outvec = Vector.zero
|
||||
for k, v in other.components.items():
|
||||
vect_dot = k.args[0].dot(self)
|
||||
outvec += vect_dot * v * k.args[1]
|
||||
return outvec
|
||||
from sympy.vector.deloperator import Del
|
||||
if not isinstance(other, (Del, Vector)):
|
||||
raise TypeError(str(other) + " is not a vector, dyadic or " +
|
||||
"del operator")
|
||||
|
||||
# Check if the other is a del operator
|
||||
if isinstance(other, Del):
|
||||
def directional_derivative(field):
|
||||
from sympy.vector.functions import directional_derivative
|
||||
return directional_derivative(field, self)
|
||||
return directional_derivative
|
||||
|
||||
return dot(self, other)
|
||||
|
||||
def __and__(self, other):
|
||||
return self.dot(other)
|
||||
|
||||
__and__.__doc__ = dot.__doc__
|
||||
|
||||
def cross(self, other):
|
||||
"""
|
||||
Returns the cross product of this Vector with another Vector or
|
||||
Dyadic instance.
|
||||
The cross product is a Vector, if 'other' is a Vector. If 'other'
|
||||
is a Dyadic, this returns a Dyadic instance.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other: Vector/Dyadic
|
||||
The Vector or Dyadic we are crossing with.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> C.i.cross(C.j)
|
||||
C.k
|
||||
>>> C.i ^ C.i
|
||||
0
|
||||
>>> v = 3*C.i + 4*C.j + 5*C.k
|
||||
>>> v ^ C.i
|
||||
5*C.j + (-4)*C.k
|
||||
>>> d = C.i.outer(C.i)
|
||||
>>> C.j.cross(d)
|
||||
(-1)*(C.k|C.i)
|
||||
|
||||
"""
|
||||
|
||||
# Check special cases
|
||||
if isinstance(other, Dyadic):
|
||||
if isinstance(self, VectorZero):
|
||||
return Dyadic.zero
|
||||
outdyad = Dyadic.zero
|
||||
for k, v in other.components.items():
|
||||
cross_product = self.cross(k.args[0])
|
||||
outer = cross_product.outer(k.args[1])
|
||||
outdyad += v * outer
|
||||
return outdyad
|
||||
|
||||
return cross(self, other)
|
||||
|
||||
def __xor__(self, other):
|
||||
return self.cross(other)
|
||||
|
||||
__xor__.__doc__ = cross.__doc__
|
||||
|
||||
def outer(self, other):
|
||||
"""
|
||||
Returns the outer product of this vector with another, in the
|
||||
form of a Dyadic instance.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
other : Vector
|
||||
The Vector with respect to which the outer product is to
|
||||
be computed.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> N = CoordSys3D('N')
|
||||
>>> N.i.outer(N.j)
|
||||
(N.i|N.j)
|
||||
|
||||
"""
|
||||
|
||||
# Handle the special cases
|
||||
if not isinstance(other, Vector):
|
||||
raise TypeError("Invalid operand for outer product")
|
||||
elif (isinstance(self, VectorZero) or
|
||||
isinstance(other, VectorZero)):
|
||||
return Dyadic.zero
|
||||
|
||||
# Iterate over components of both the vectors to generate
|
||||
# the required Dyadic instance
|
||||
args = [(v1 * v2) * BaseDyadic(k1, k2) for (k1, v1), (k2, v2)
|
||||
in product(self.components.items(), other.components.items())]
|
||||
|
||||
return DyadicAdd(*args)
|
||||
|
||||
def projection(self, other, scalar=False):
|
||||
"""
|
||||
Returns the vector or scalar projection of the 'other' on 'self'.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector.coordsysrect import CoordSys3D
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> i, j, k = C.base_vectors()
|
||||
>>> v1 = i + j + k
|
||||
>>> v2 = 3*i + 4*j
|
||||
>>> v1.projection(v2)
|
||||
7/3*C.i + 7/3*C.j + 7/3*C.k
|
||||
>>> v1.projection(v2, scalar=True)
|
||||
7/3
|
||||
|
||||
"""
|
||||
if self.equals(Vector.zero):
|
||||
return S.Zero if scalar else Vector.zero
|
||||
|
||||
if scalar:
|
||||
return self.dot(other) / self.dot(self)
|
||||
else:
|
||||
return self.dot(other) / self.dot(self) * self
|
||||
|
||||
@property
|
||||
def _projections(self):
|
||||
"""
|
||||
Returns the components of this vector but the output includes
|
||||
also zero values components.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Vector
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> v1 = 3*C.i + 4*C.j + 5*C.k
|
||||
>>> v1._projections
|
||||
(3, 4, 5)
|
||||
>>> v2 = C.x*C.y*C.z*C.i
|
||||
>>> v2._projections
|
||||
(C.x*C.y*C.z, 0, 0)
|
||||
>>> v3 = Vector.zero
|
||||
>>> v3._projections
|
||||
(0, 0, 0)
|
||||
"""
|
||||
|
||||
from sympy.vector.operators import _get_coord_systems
|
||||
if isinstance(self, VectorZero):
|
||||
return (S.Zero, S.Zero, S.Zero)
|
||||
base_vec = next(iter(_get_coord_systems(self))).base_vectors()
|
||||
return tuple([self.dot(i) for i in base_vec])
|
||||
|
||||
def __or__(self, other):
|
||||
return self.outer(other)
|
||||
|
||||
__or__.__doc__ = outer.__doc__
|
||||
|
||||
def to_matrix(self, system):
|
||||
"""
|
||||
Returns the matrix form of this vector with respect to the
|
||||
specified coordinate system.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
system : CoordSys3D
|
||||
The system wrt which the matrix form is to be computed
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> C = CoordSys3D('C')
|
||||
>>> from sympy.abc import a, b, c
|
||||
>>> v = a*C.i + b*C.j + c*C.k
|
||||
>>> v.to_matrix(C)
|
||||
Matrix([
|
||||
[a],
|
||||
[b],
|
||||
[c]])
|
||||
|
||||
"""
|
||||
|
||||
return Matrix([self.dot(unit_vec) for unit_vec in
|
||||
system.base_vectors()])
|
||||
|
||||
def separate(self):
|
||||
"""
|
||||
The constituents of this vector in different coordinate systems,
|
||||
as per its definition.
|
||||
|
||||
Returns a dict mapping each CoordSys3D to the corresponding
|
||||
constituent Vector.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> R1 = CoordSys3D('R1')
|
||||
>>> R2 = CoordSys3D('R2')
|
||||
>>> v = R1.i + R2.i
|
||||
>>> v.separate() == {R1: R1.i, R2: R2.i}
|
||||
True
|
||||
|
||||
"""
|
||||
|
||||
parts = {}
|
||||
for vect, measure in self.components.items():
|
||||
parts[vect.system] = (parts.get(vect.system, Vector.zero) +
|
||||
vect * measure)
|
||||
return parts
|
||||
|
||||
def _div_helper(one, other):
|
||||
""" Helper for division involving vectors. """
|
||||
if isinstance(one, Vector) and isinstance(other, Vector):
|
||||
raise TypeError("Cannot divide two vectors")
|
||||
elif isinstance(one, Vector):
|
||||
if other == S.Zero:
|
||||
raise ValueError("Cannot divide a vector by zero")
|
||||
return VectorMul(one, Pow(other, S.NegativeOne))
|
||||
else:
|
||||
raise TypeError("Invalid division involving a vector")
|
||||
|
||||
# The following is adapted from the matrices.expressions.matexpr file
|
||||
|
||||
def get_postprocessor(cls):
|
||||
def _postprocessor(expr):
|
||||
vec_class = {Add: VectorAdd}[cls]
|
||||
vectors = []
|
||||
for term in expr.args:
|
||||
if isinstance(term.kind, VectorKind):
|
||||
vectors.append(term)
|
||||
|
||||
if vec_class == VectorAdd:
|
||||
return VectorAdd(*vectors).doit(deep=False)
|
||||
return _postprocessor
|
||||
|
||||
|
||||
Basic._constructor_postprocessor_mapping[Vector] = {
|
||||
"Add": [get_postprocessor(Add)],
|
||||
}
|
||||
|
||||
class BaseVector(Vector, AtomicExpr):
|
||||
"""
|
||||
Class to denote a base vector.
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, index, system, pretty_str=None, latex_str=None):
|
||||
if pretty_str is None:
|
||||
pretty_str = "x{}".format(index)
|
||||
if latex_str is None:
|
||||
latex_str = "x_{}".format(index)
|
||||
pretty_str = str(pretty_str)
|
||||
latex_str = str(latex_str)
|
||||
# Verify arguments
|
||||
if index not in range(0, 3):
|
||||
raise ValueError("index must be 0, 1 or 2")
|
||||
if not isinstance(system, CoordSys3D):
|
||||
raise TypeError("system should be a CoordSys3D")
|
||||
name = system._vector_names[index]
|
||||
# Initialize an object
|
||||
obj = super().__new__(cls, S(index), system)
|
||||
# Assign important attributes
|
||||
obj._base_instance = obj
|
||||
obj._components = {obj: S.One}
|
||||
obj._measure_number = S.One
|
||||
obj._name = system._name + '.' + name
|
||||
obj._pretty_form = '' + pretty_str
|
||||
obj._latex_form = latex_str
|
||||
obj._system = system
|
||||
# The _id is used for printing purposes
|
||||
obj._id = (index, system)
|
||||
assumptions = {'commutative': True}
|
||||
obj._assumptions = StdFactKB(assumptions)
|
||||
|
||||
# This attr is used for re-expression to one of the systems
|
||||
# involved in the definition of the Vector. Applies to
|
||||
# VectorMul and VectorAdd too.
|
||||
obj._sys = system
|
||||
|
||||
return obj
|
||||
|
||||
@property
|
||||
def system(self):
|
||||
return self._system
|
||||
|
||||
def _sympystr(self, printer):
|
||||
return self._name
|
||||
|
||||
def _sympyrepr(self, printer):
|
||||
index, system = self._id
|
||||
return printer._print(system) + '.' + system._vector_names[index]
|
||||
|
||||
@property
|
||||
def free_symbols(self):
|
||||
return {self}
|
||||
|
||||
def _eval_conjugate(self):
|
||||
return self
|
||||
|
||||
|
||||
class VectorAdd(BasisDependentAdd, Vector):
|
||||
"""
|
||||
Class to denote sum of Vector instances.
|
||||
"""
|
||||
|
||||
def __new__(cls, *args, **options):
|
||||
obj = BasisDependentAdd.__new__(cls, *args, **options)
|
||||
return obj
|
||||
|
||||
def _sympystr(self, printer):
|
||||
ret_str = ''
|
||||
items = list(self.separate().items())
|
||||
items.sort(key=lambda x: x[0].__str__())
|
||||
for system, vect in items:
|
||||
base_vects = system.base_vectors()
|
||||
for x in base_vects:
|
||||
if x in vect.components:
|
||||
temp_vect = self.components[x] * x
|
||||
ret_str += printer._print(temp_vect) + " + "
|
||||
return ret_str[:-3]
|
||||
|
||||
|
||||
class VectorMul(BasisDependentMul, Vector):
|
||||
"""
|
||||
Class to denote products of scalars and BaseVectors.
|
||||
"""
|
||||
|
||||
def __new__(cls, *args, **options):
|
||||
obj = BasisDependentMul.__new__(cls, *args, **options)
|
||||
return obj
|
||||
|
||||
@property
|
||||
def base_vector(self):
|
||||
""" The BaseVector involved in the product. """
|
||||
return self._base_instance
|
||||
|
||||
@property
|
||||
def measure_number(self):
|
||||
""" The scalar expression involved in the definition of
|
||||
this VectorMul.
|
||||
"""
|
||||
return self._measure_number
|
||||
|
||||
|
||||
class VectorZero(BasisDependentZero, Vector):
|
||||
"""
|
||||
Class to denote a zero vector
|
||||
"""
|
||||
|
||||
_op_priority = 12.1
|
||||
_pretty_form = '0'
|
||||
_latex_form = r'\mathbf{\hat{0}}'
|
||||
|
||||
def __new__(cls):
|
||||
obj = BasisDependentZero.__new__(cls)
|
||||
return obj
|
||||
|
||||
|
||||
class Cross(Vector):
|
||||
"""
|
||||
Represents unevaluated Cross product.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Cross
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> v1 = R.i + R.j + R.k
|
||||
>>> v2 = R.x * R.i + R.y * R.j + R.z * R.k
|
||||
>>> Cross(v1, v2)
|
||||
Cross(R.i + R.j + R.k, R.x*R.i + R.y*R.j + R.z*R.k)
|
||||
>>> Cross(v1, v2).doit()
|
||||
(-R.y + R.z)*R.i + (R.x - R.z)*R.j + (-R.x + R.y)*R.k
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, expr1, expr2):
|
||||
expr1 = sympify(expr1)
|
||||
expr2 = sympify(expr2)
|
||||
if default_sort_key(expr1) > default_sort_key(expr2):
|
||||
return -Cross(expr2, expr1)
|
||||
obj = Expr.__new__(cls, expr1, expr2)
|
||||
obj._expr1 = expr1
|
||||
obj._expr2 = expr2
|
||||
return obj
|
||||
|
||||
def doit(self, **hints):
|
||||
return cross(self._expr1, self._expr2)
|
||||
|
||||
|
||||
class Dot(Expr):
|
||||
"""
|
||||
Represents unevaluated Dot product.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D, Dot
|
||||
>>> from sympy import symbols
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> a, b, c = symbols('a b c')
|
||||
>>> v1 = R.i + R.j + R.k
|
||||
>>> v2 = a * R.i + b * R.j + c * R.k
|
||||
>>> Dot(v1, v2)
|
||||
Dot(R.i + R.j + R.k, a*R.i + b*R.j + c*R.k)
|
||||
>>> Dot(v1, v2).doit()
|
||||
a + b + c
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, expr1, expr2):
|
||||
expr1 = sympify(expr1)
|
||||
expr2 = sympify(expr2)
|
||||
expr1, expr2 = sorted([expr1, expr2], key=default_sort_key)
|
||||
obj = Expr.__new__(cls, expr1, expr2)
|
||||
obj._expr1 = expr1
|
||||
obj._expr2 = expr2
|
||||
return obj
|
||||
|
||||
def doit(self, **hints):
|
||||
return dot(self._expr1, self._expr2)
|
||||
|
||||
|
||||
def cross(vect1, vect2):
|
||||
"""
|
||||
Returns cross product of two vectors.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy.vector.vector import cross
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> v1 = R.i + R.j + R.k
|
||||
>>> v2 = R.x * R.i + R.y * R.j + R.z * R.k
|
||||
>>> cross(v1, v2)
|
||||
(-R.y + R.z)*R.i + (R.x - R.z)*R.j + (-R.x + R.y)*R.k
|
||||
|
||||
"""
|
||||
if isinstance(vect1, Add):
|
||||
return VectorAdd.fromiter(cross(i, vect2) for i in vect1.args)
|
||||
if isinstance(vect2, Add):
|
||||
return VectorAdd.fromiter(cross(vect1, i) for i in vect2.args)
|
||||
if isinstance(vect1, BaseVector) and isinstance(vect2, BaseVector):
|
||||
if vect1._sys == vect2._sys:
|
||||
n1 = vect1.args[0]
|
||||
n2 = vect2.args[0]
|
||||
if n1 == n2:
|
||||
return Vector.zero
|
||||
n3 = ({0,1,2}.difference({n1, n2})).pop()
|
||||
sign = 1 if ((n1 + 1) % 3 == n2) else -1
|
||||
return sign*vect1._sys.base_vectors()[n3]
|
||||
from .functions import express
|
||||
try:
|
||||
v = express(vect1, vect2._sys)
|
||||
except ValueError:
|
||||
return Cross(vect1, vect2)
|
||||
else:
|
||||
return cross(v, vect2)
|
||||
if isinstance(vect1, VectorZero) or isinstance(vect2, VectorZero):
|
||||
return Vector.zero
|
||||
if isinstance(vect1, VectorMul):
|
||||
v1, m1 = next(iter(vect1.components.items()))
|
||||
return m1*cross(v1, vect2)
|
||||
if isinstance(vect2, VectorMul):
|
||||
v2, m2 = next(iter(vect2.components.items()))
|
||||
return m2*cross(vect1, v2)
|
||||
|
||||
return Cross(vect1, vect2)
|
||||
|
||||
|
||||
def dot(vect1, vect2):
|
||||
"""
|
||||
Returns dot product of two vectors.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.vector import CoordSys3D
|
||||
>>> from sympy.vector.vector import dot
|
||||
>>> R = CoordSys3D('R')
|
||||
>>> v1 = R.i + R.j + R.k
|
||||
>>> v2 = R.x * R.i + R.y * R.j + R.z * R.k
|
||||
>>> dot(v1, v2)
|
||||
R.x + R.y + R.z
|
||||
|
||||
"""
|
||||
if isinstance(vect1, Add):
|
||||
return Add.fromiter(dot(i, vect2) for i in vect1.args)
|
||||
if isinstance(vect2, Add):
|
||||
return Add.fromiter(dot(vect1, i) for i in vect2.args)
|
||||
if isinstance(vect1, BaseVector) and isinstance(vect2, BaseVector):
|
||||
if vect1._sys == vect2._sys:
|
||||
return S.One if vect1 == vect2 else S.Zero
|
||||
from .functions import express
|
||||
try:
|
||||
v = express(vect2, vect1._sys)
|
||||
except ValueError:
|
||||
return Dot(vect1, vect2)
|
||||
else:
|
||||
return dot(vect1, v)
|
||||
if isinstance(vect1, VectorZero) or isinstance(vect2, VectorZero):
|
||||
return S.Zero
|
||||
if isinstance(vect1, VectorMul):
|
||||
v1, m1 = next(iter(vect1.components.items()))
|
||||
return m1*dot(v1, vect2)
|
||||
if isinstance(vect2, VectorMul):
|
||||
v2, m2 = next(iter(vect2.components.items()))
|
||||
return m2*dot(vect1, v2)
|
||||
|
||||
return Dot(vect1, vect2)
|
||||
|
||||
|
||||
Vector._expr_type = Vector
|
||||
Vector._mul_func = VectorMul
|
||||
Vector._add_func = VectorAdd
|
||||
Vector._zero_func = VectorZero
|
||||
Vector._base_func = BaseVector
|
||||
Vector.zero = VectorZero()
|
||||
Reference in New Issue
Block a user