I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,47 @@
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)
__all__ = [
'Vector', 'VectorAdd', 'VectorMul', 'BaseVector', 'VectorZero', 'Cross',
'Dot', 'cross', 'dot',
'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',
]

View File

@ -0,0 +1,365 @@
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
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."""
l = [x * self.components[x] for x in self.components]
return 0, tuple(l)
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
if hasattr(arg, "components"):
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):
from sympy.vector import Cross, Dot, Curl, Gradient
count = 0
measure_number = S.One
zeroflag = False
extra_args = []
# Determine the component and check arguments
# Also keep a count to ensure two vectors aren't
# being multiplied
for arg in args:
if isinstance(arg, cls._zero_func):
count += 1
zeroflag = True
elif arg == S.Zero:
zeroflag = True
elif isinstance(arg, (cls._base_func, cls._mul_func)):
count += 1
expr = arg._base_instance
measure_number *= arg._measure_number
elif isinstance(arg, cls._add_func):
count += 1
expr = arg
elif isinstance(arg, (Cross, Dot, Curl, Gradient)):
extra_args.append(arg)
else:
measure_number *= arg
# Make sure incompatible types weren't multiplied
if count > 1:
raise ValueError("Invalid multiplication")
elif count == 0:
return Mul(*args, **options)
# Handle zero vector case
if zeroflag:
return cls.zero
# If one of the args was a VectorAdd, return an
# appropriate VectorAdd instance
if isinstance(expr, cls._add_func):
newargs = [cls._mul_func(measure_number, x) for
x in expr.args]
return cls._add_func(*newargs)
obj = super().__new__(cls, measure_number,
expr._base_instance,
*extra_args,
**options)
if isinstance(obj, Add):
return cls._add_func(*obj.args)
obj._base_instance = expr._base_instance
obj._measure_number = measure_number
assumptions = {'commutative': True}
obj._assumptions = StdFactKB(assumptions)
obj._components = {expr._base_instance: measure_number}
obj._sys = expr._base_instance._sys
return obj
def _sympystr(self, printer):
measure_str = printer._print(self._measure_number)
if ('(' in measure_str or '-' in measure_str or
'+' in measure_str):
measure_str = '(' + measure_str + ')'
return measure_str + '*' + printer._print(self._base_instance)
class BasisDependentZero(BasisDependent):
"""
Class to denote a zero basis dependent instance.
"""
components: dict['BaseVector', Expr] = {}
_latex_form: str
def __new__(cls):
obj = super().__new__(cls)
# Pre-compute a specific hash value for the zero vector
# Use the same one always
obj._hash = (S.Zero, cls).__hash__()
return obj
def __hash__(self):
return self._hash
@call_highest_priority('__req__')
def __eq__(self, other):
return isinstance(other, self._zero_func)
__req__ = __eq__
@call_highest_priority('__radd__')
def __add__(self, other):
if isinstance(other, self._expr_type):
return other
else:
raise TypeError("Invalid argument types for addition")
@call_highest_priority('__add__')
def __radd__(self, other):
if isinstance(other, self._expr_type):
return other
else:
raise TypeError("Invalid argument types for addition")
@call_highest_priority('__rsub__')
def __sub__(self, other):
if isinstance(other, self._expr_type):
return -other
else:
raise TypeError("Invalid argument types for subtraction")
@call_highest_priority('__sub__')
def __rsub__(self, other):
if isinstance(other, self._expr_type):
return other
else:
raise TypeError("Invalid argument types for subtraction")
def __neg__(self):
return self
def normalize(self):
"""
Returns the normalized version of this vector.
"""
return self
def _sympystr(self, printer):
return '0'

File diff suppressed because it is too large Load Diff

View 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

View 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()

View File

@ -0,0 +1,517 @@
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.simplify.simplify import simplify
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)
i = other_path.index(obj)
while i >= 0:
from_path.append(other_path[i])
i -= 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 simplify(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

View 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

View 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)

View 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 Curl(vect)
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 Divergence(vect)
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

View 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)

View File

@ -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 intergation
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)]

View File

@ -0,0 +1,151 @@
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
i = -1
for i in range(rootindex):
result += path[i]._pos
i += 2
while i < len(path):
result -= path[i]._pos
i += 1
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

View File

@ -0,0 +1,69 @@
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
class BaseScalar(AtomicExpr):
"""
A coordinate symbol/base scalar.
Ideally, users should not instantiate this class.
"""
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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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))

View File

@ -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())

View File

@ -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 - C.z, 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))

View File

@ -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))))'

View File

@ -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))

View File

@ -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}}"

View File

@ -0,0 +1,266 @@
from sympy.core import Rational, S
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
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)
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(v1, v2).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_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_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

View File

@ -0,0 +1,623 @@
from __future__ import annotations
from itertools import product
from sympy.core.add import Add
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
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
@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 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")
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}
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()