Source code for sympy.vector.operators

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, dot
from sympy.vector.scalar import BaseScalar
from sympy.utilities.exceptions import SymPyDeprecationWarning
from sympy.core.function import Derivative
from sympy import Add, 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 _get_coord_sys_from_expr(expr, coord_sys=None):
    """
    expr : expression
        The coordinate system is extracted from this parameter.
    """

    # TODO: Remove this line when warning from issue #12884 will be removed
    if coord_sys is not None:
        SymPyDeprecationWarning(
            feature="coord_sys parameter",
            useinstead="do not use it",
            deprecated_since_version="1.1",
            issue=12884,
        ).warn()

    return _get_coord_systems(expr)


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, **kwargs):
        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, **kwargs):
        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, **kwargs):
        return curl(self._expr, doit=True)


[docs]def curl(vect, coord_sys=None, 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 coord_sys : CoordSys3D The coordinate system to calculate the gradient in. Deprecated since version 1.1 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_sys_from_expr(vect, coord_sys) 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)
[docs]def divergence(vect, coord_sys=None, 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 coord_sys : CoordSys3D The coordinate system to calculate the gradient in Deprecated since version 1.1 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_sys_from_expr(vect, coord_sys) 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)
[docs]def gradient(scalar_field, coord_sys=None, 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 coord_sys : CoordSys3D The coordinate system to calculate the gradient in Deprecated since version 1.1 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_sys_from_expr(scalar_field, coord_sys) 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, **kwargs): 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 S(0) """ from sympy.vector.functions import express new_expr = express(expr, base_scalar.system, variables=True) if base_scalar in new_expr.atoms(BaseScalar): return Derivative(coeff_1 * coeff_2 * new_expr, base_scalar) return S(0)