| import collections |
| from sympy.core.expr import Expr |
| from sympy.core import sympify, S, preorder_traversal |
| from sympy.vector.coordsysrect import CoordSys3D |
| from sympy.vector.vector import Vector, VectorMul, VectorAdd, Cross, Dot |
| from sympy.core.function import Derivative |
| from sympy.core.add import Add |
| from sympy.core.mul import Mul |
|
|
|
|
| def _get_coord_systems(expr): |
| g = preorder_traversal(expr) |
| ret = set() |
| for i in g: |
| if isinstance(i, CoordSys3D): |
| ret.add(i) |
| g.skip() |
| return frozenset(ret) |
|
|
|
|
| def _split_mul_args_wrt_coordsys(expr): |
| d = collections.defaultdict(lambda: S.One) |
| for i in expr.args: |
| d[_get_coord_systems(i)] *= i |
| return list(d.values()) |
|
|
|
|
| class Gradient(Expr): |
| """ |
| Represents unevaluated Gradient. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.vector import CoordSys3D, Gradient |
| >>> R = CoordSys3D('R') |
| >>> s = R.x*R.y*R.z |
| >>> Gradient(s) |
| Gradient(R.x*R.y*R.z) |
| |
| """ |
|
|
| def __new__(cls, expr): |
| expr = sympify(expr) |
| obj = Expr.__new__(cls, expr) |
| obj._expr = expr |
| return obj |
|
|
| def doit(self, **hints): |
| return gradient(self._expr, doit=True) |
|
|
|
|
| class Divergence(Expr): |
| """ |
| Represents unevaluated Divergence. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.vector import CoordSys3D, Divergence |
| >>> R = CoordSys3D('R') |
| >>> v = R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k |
| >>> Divergence(v) |
| Divergence(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k) |
| |
| """ |
|
|
| def __new__(cls, expr): |
| expr = sympify(expr) |
| obj = Expr.__new__(cls, expr) |
| obj._expr = expr |
| return obj |
|
|
| def doit(self, **hints): |
| return divergence(self._expr, doit=True) |
|
|
|
|
| class Curl(Expr): |
| """ |
| Represents unevaluated Curl. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.vector import CoordSys3D, Curl |
| >>> R = CoordSys3D('R') |
| >>> v = R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k |
| >>> Curl(v) |
| Curl(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k) |
| |
| """ |
|
|
| def __new__(cls, expr): |
| expr = sympify(expr) |
| obj = Expr.__new__(cls, expr) |
| obj._expr = expr |
| return obj |
|
|
| def doit(self, **hints): |
| return curl(self._expr, doit=True) |
|
|
|
|
| def curl(vect, doit=True): |
| """ |
| Returns the curl of a vector field computed wrt the base scalars |
| of the given coordinate system. |
| |
| Parameters |
| ========== |
| |
| vect : Vector |
| The vector operand |
| |
| doit : bool |
| If True, the result is returned after calling .doit() on |
| each component. Else, the returned expression contains |
| Derivative instances |
| |
| Examples |
| ======== |
| |
| >>> from sympy.vector import CoordSys3D, curl |
| >>> R = CoordSys3D('R') |
| >>> v1 = R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k |
| >>> curl(v1) |
| 0 |
| >>> v2 = R.x*R.y*R.z*R.i |
| >>> curl(v2) |
| R.x*R.y*R.j + (-R.x*R.z)*R.k |
| |
| """ |
|
|
| coord_sys = _get_coord_systems(vect) |
|
|
| if len(coord_sys) == 0: |
| return Vector.zero |
| elif len(coord_sys) == 1: |
| coord_sys = next(iter(coord_sys)) |
| i, j, k = coord_sys.base_vectors() |
| x, y, z = coord_sys.base_scalars() |
| h1, h2, h3 = coord_sys.lame_coefficients() |
| vectx = vect.dot(i) |
| vecty = vect.dot(j) |
| vectz = vect.dot(k) |
| outvec = Vector.zero |
| outvec += (Derivative(vectz * h3, y) - |
| Derivative(vecty * h2, z)) * i / (h2 * h3) |
| outvec += (Derivative(vectx * h1, z) - |
| Derivative(vectz * h3, x)) * j / (h1 * h3) |
| outvec += (Derivative(vecty * h2, x) - |
| Derivative(vectx * h1, y)) * k / (h2 * h1) |
|
|
| if doit: |
| return outvec.doit() |
| return outvec |
| else: |
| if isinstance(vect, (Add, VectorAdd)): |
| from sympy.vector import express |
| try: |
| cs = next(iter(coord_sys)) |
| args = [express(i, cs, variables=True) for i in vect.args] |
| except ValueError: |
| args = vect.args |
| return VectorAdd.fromiter(curl(i, doit=doit) for i in args) |
| elif isinstance(vect, (Mul, VectorMul)): |
| vector = [i for i in vect.args if isinstance(i, (Vector, Cross, Gradient))][0] |
| scalar = Mul.fromiter(i for i in vect.args if not isinstance(i, (Vector, Cross, Gradient))) |
| res = Cross(gradient(scalar), vector).doit() + scalar*curl(vector, doit=doit) |
| if doit: |
| return res.doit() |
| return res |
| elif isinstance(vect, (Cross, Curl, Gradient)): |
| return Curl(vect) |
| else: |
| raise ValueError("Invalid argument for curl") |
|
|
|
|
| def divergence(vect, doit=True): |
| """ |
| Returns the divergence of a vector field computed wrt the base |
| scalars of the given coordinate system. |
| |
| Parameters |
| ========== |
| |
| vector : Vector |
| The vector operand |
| |
| doit : bool |
| If True, the result is returned after calling .doit() on |
| each component. Else, the returned expression contains |
| Derivative instances |
| |
| Examples |
| ======== |
| |
| >>> from sympy.vector import CoordSys3D, divergence |
| >>> R = CoordSys3D('R') |
| >>> v1 = R.x*R.y*R.z * (R.i+R.j+R.k) |
| |
| >>> divergence(v1) |
| R.x*R.y + R.x*R.z + R.y*R.z |
| >>> v2 = 2*R.y*R.z*R.j |
| >>> divergence(v2) |
| 2*R.z |
| |
| """ |
| coord_sys = _get_coord_systems(vect) |
| if len(coord_sys) == 0: |
| return S.Zero |
| elif len(coord_sys) == 1: |
| if isinstance(vect, (Cross, Curl, Gradient)): |
| return Divergence(vect) |
| |
| coord_sys = next(iter(coord_sys)) |
| i, j, k = coord_sys.base_vectors() |
| x, y, z = coord_sys.base_scalars() |
| h1, h2, h3 = coord_sys.lame_coefficients() |
| vx = _diff_conditional(vect.dot(i), x, h2, h3) \ |
| / (h1 * h2 * h3) |
| vy = _diff_conditional(vect.dot(j), y, h3, h1) \ |
| / (h1 * h2 * h3) |
| vz = _diff_conditional(vect.dot(k), z, h1, h2) \ |
| / (h1 * h2 * h3) |
| res = vx + vy + vz |
| if doit: |
| return res.doit() |
| return res |
| else: |
| if isinstance(vect, (Add, VectorAdd)): |
| return Add.fromiter(divergence(i, doit=doit) for i in vect.args) |
| elif isinstance(vect, (Mul, VectorMul)): |
| vector = [i for i in vect.args if isinstance(i, (Vector, Cross, Gradient))][0] |
| scalar = Mul.fromiter(i for i in vect.args if not isinstance(i, (Vector, Cross, Gradient))) |
| res = Dot(vector, gradient(scalar)) + scalar*divergence(vector, doit=doit) |
| if doit: |
| return res.doit() |
| return res |
| elif isinstance(vect, (Cross, Curl, Gradient)): |
| return Divergence(vect) |
| else: |
| raise ValueError("Invalid argument for divergence") |
|
|
|
|
| def gradient(scalar_field, doit=True): |
| """ |
| Returns the vector gradient of a scalar field computed wrt the |
| base scalars of the given coordinate system. |
| |
| Parameters |
| ========== |
| |
| scalar_field : SymPy Expr |
| The scalar field to compute the gradient of |
| |
| doit : bool |
| If True, the result is returned after calling .doit() on |
| each component. Else, the returned expression contains |
| Derivative instances |
| |
| Examples |
| ======== |
| |
| >>> from sympy.vector import CoordSys3D, gradient |
| >>> R = CoordSys3D('R') |
| >>> s1 = R.x*R.y*R.z |
| >>> gradient(s1) |
| R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k |
| >>> s2 = 5*R.x**2*R.z |
| >>> gradient(s2) |
| 10*R.x*R.z*R.i + 5*R.x**2*R.k |
| |
| """ |
| coord_sys = _get_coord_systems(scalar_field) |
|
|
| if len(coord_sys) == 0: |
| return Vector.zero |
| elif len(coord_sys) == 1: |
| coord_sys = next(iter(coord_sys)) |
| h1, h2, h3 = coord_sys.lame_coefficients() |
| i, j, k = coord_sys.base_vectors() |
| x, y, z = coord_sys.base_scalars() |
| vx = Derivative(scalar_field, x) / h1 |
| vy = Derivative(scalar_field, y) / h2 |
| vz = Derivative(scalar_field, z) / h3 |
|
|
| if doit: |
| return (vx * i + vy * j + vz * k).doit() |
| return vx * i + vy * j + vz * k |
| else: |
| if isinstance(scalar_field, (Add, VectorAdd)): |
| return VectorAdd.fromiter(gradient(i) for i in scalar_field.args) |
| if isinstance(scalar_field, (Mul, VectorMul)): |
| s = _split_mul_args_wrt_coordsys(scalar_field) |
| return VectorAdd.fromiter(scalar_field / i * gradient(i) for i in s) |
| return Gradient(scalar_field) |
|
|
|
|
| class Laplacian(Expr): |
| """ |
| Represents unevaluated Laplacian. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.vector import CoordSys3D, Laplacian |
| >>> R = CoordSys3D('R') |
| >>> v = 3*R.x**3*R.y**2*R.z**3 |
| >>> Laplacian(v) |
| Laplacian(3*R.x**3*R.y**2*R.z**3) |
| |
| """ |
|
|
| def __new__(cls, expr): |
| expr = sympify(expr) |
| obj = Expr.__new__(cls, expr) |
| obj._expr = expr |
| return obj |
|
|
| def doit(self, **hints): |
| from sympy.vector.functions import laplacian |
| return laplacian(self._expr) |
|
|
|
|
| def _diff_conditional(expr, base_scalar, coeff_1, coeff_2): |
| """ |
| First re-expresses expr in the system that base_scalar belongs to. |
| If base_scalar appears in the re-expressed form, differentiates |
| it wrt base_scalar. |
| Else, returns 0 |
| """ |
| from sympy.vector.functions import express |
| new_expr = express(expr, base_scalar.system, variables=True) |
| arg = coeff_1 * coeff_2 * new_expr |
| return Derivative(arg, base_scalar) if arg else S.Zero |
|
|