Initialisation du repository de Beta
This commit is contained in:
commit
14985f6dbb
9469 changed files with 1903273 additions and 0 deletions
50
venv/lib/python3.12/site-packages/sympy/vector/__init__.py
Normal file
50
venv/lib/python3.12/site-packages/sympy/vector/__init__.py
Normal file
|
|
@ -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',
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
374
venv/lib/python3.12/site-packages/sympy/vector/basisdependent.py
Normal file
374
venv/lib/python3.12/site-packages/sympy/vector/basisdependent.py
Normal file
|
|
@ -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'
|
||||
1031
venv/lib/python3.12/site-packages/sympy/vector/coordsysrect.py
Normal file
1031
venv/lib/python3.12/site-packages/sympy/vector/coordsysrect.py
Normal file
File diff suppressed because it is too large
Load diff
121
venv/lib/python3.12/site-packages/sympy/vector/deloperator.py
Normal file
121
venv/lib/python3.12/site-packages/sympy/vector/deloperator.py
Normal file
|
|
@ -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
|
||||
285
venv/lib/python3.12/site-packages/sympy/vector/dyadic.py
Normal file
285
venv/lib/python3.12/site-packages/sympy/vector/dyadic.py
Normal file
|
|
@ -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()
|
||||
513
venv/lib/python3.12/site-packages/sympy/vector/functions.py
Normal file
513
venv/lib/python3.12/site-packages/sympy/vector/functions.py
Normal file
|
|
@ -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
|
||||
506
venv/lib/python3.12/site-packages/sympy/vector/implicitregion.py
Normal file
506
venv/lib/python3.12/site-packages/sympy/vector/implicitregion.py
Normal file
|
|
@ -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
|
||||
206
venv/lib/python3.12/site-packages/sympy/vector/integrals.py
Normal file
206
venv/lib/python3.12/site-packages/sympy/vector/integrals.py
Normal file
|
|
@ -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)
|
||||
67
venv/lib/python3.12/site-packages/sympy/vector/kind.py
Normal file
67
venv/lib/python3.12/site-packages/sympy/vector/kind.py
Normal file
|
|
@ -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)
|
||||
335
venv/lib/python3.12/site-packages/sympy/vector/operators.py
Normal file
335
venv/lib/python3.12/site-packages/sympy/vector/operators.py
Normal file
|
|
@ -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
|
||||
398
venv/lib/python3.12/site-packages/sympy/vector/orienters.py
Normal file
398
venv/lib/python3.12/site-packages/sympy/vector/orienters.py
Normal file
|
|
@ -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)]
|
||||
148
venv/lib/python3.12/site-packages/sympy/vector/point.py
Normal file
148
venv/lib/python3.12/site-packages/sympy/vector/point.py
Normal file
|
|
@ -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
|
||||
72
venv/lib/python3.12/site-packages/sympy/vector/scalar.py
Normal file
72
venv/lib/python3.12/site-packages/sympy/vector/scalar.py
Normal file
|
|
@ -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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
714
venv/lib/python3.12/site-packages/sympy/vector/vector.py
Normal file
714
venv/lib/python3.12/site-packages/sympy/vector/vector.py
Normal file
|
|
@ -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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue