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


[docs] 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)
[docs] 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)
[docs] 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