Spaces:
Sleeping
Sleeping
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 | |