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