314 lines
8.4 KiB
Python
314 lines
8.4 KiB
Python
|
from sympy.core.function import diff
|
||
|
from sympy.core.singleton import S
|
||
|
from sympy.integrals.integrals import integrate
|
||
|
from sympy.physics.vector import Vector, express
|
||
|
from sympy.physics.vector.frame import _check_frame
|
||
|
from sympy.physics.vector.vector import _check_vector
|
||
|
|
||
|
|
||
|
__all__ = ['curl', 'divergence', 'gradient', 'is_conservative',
|
||
|
'is_solenoidal', 'scalar_potential',
|
||
|
'scalar_potential_difference']
|
||
|
|
||
|
|
||
|
def curl(vect, frame):
|
||
|
"""
|
||
|
Returns the curl of a vector field computed wrt the coordinate
|
||
|
symbols of the given frame.
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
vect : Vector
|
||
|
The vector operand
|
||
|
|
||
|
frame : ReferenceFrame
|
||
|
The reference frame to calculate the curl in
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.physics.vector import ReferenceFrame
|
||
|
>>> from sympy.physics.vector import curl
|
||
|
>>> R = ReferenceFrame('R')
|
||
|
>>> v1 = R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z
|
||
|
>>> curl(v1, R)
|
||
|
0
|
||
|
>>> v2 = R[0]*R[1]*R[2]*R.x
|
||
|
>>> curl(v2, R)
|
||
|
R_x*R_y*R.y - R_x*R_z*R.z
|
||
|
|
||
|
"""
|
||
|
|
||
|
_check_vector(vect)
|
||
|
if vect == 0:
|
||
|
return Vector(0)
|
||
|
vect = express(vect, frame, variables=True)
|
||
|
# A mechanical approach to avoid looping overheads
|
||
|
vectx = vect.dot(frame.x)
|
||
|
vecty = vect.dot(frame.y)
|
||
|
vectz = vect.dot(frame.z)
|
||
|
outvec = Vector(0)
|
||
|
outvec += (diff(vectz, frame[1]) - diff(vecty, frame[2])) * frame.x
|
||
|
outvec += (diff(vectx, frame[2]) - diff(vectz, frame[0])) * frame.y
|
||
|
outvec += (diff(vecty, frame[0]) - diff(vectx, frame[1])) * frame.z
|
||
|
return outvec
|
||
|
|
||
|
|
||
|
def divergence(vect, frame):
|
||
|
"""
|
||
|
Returns the divergence of a vector field computed wrt the coordinate
|
||
|
symbols of the given frame.
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
vect : Vector
|
||
|
The vector operand
|
||
|
|
||
|
frame : ReferenceFrame
|
||
|
The reference frame to calculate the divergence in
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.physics.vector import ReferenceFrame
|
||
|
>>> from sympy.physics.vector import divergence
|
||
|
>>> R = ReferenceFrame('R')
|
||
|
>>> v1 = R[0]*R[1]*R[2] * (R.x+R.y+R.z)
|
||
|
>>> divergence(v1, R)
|
||
|
R_x*R_y + R_x*R_z + R_y*R_z
|
||
|
>>> v2 = 2*R[1]*R[2]*R.y
|
||
|
>>> divergence(v2, R)
|
||
|
2*R_z
|
||
|
|
||
|
"""
|
||
|
|
||
|
_check_vector(vect)
|
||
|
if vect == 0:
|
||
|
return S.Zero
|
||
|
vect = express(vect, frame, variables=True)
|
||
|
vectx = vect.dot(frame.x)
|
||
|
vecty = vect.dot(frame.y)
|
||
|
vectz = vect.dot(frame.z)
|
||
|
out = S.Zero
|
||
|
out += diff(vectx, frame[0])
|
||
|
out += diff(vecty, frame[1])
|
||
|
out += diff(vectz, frame[2])
|
||
|
return out
|
||
|
|
||
|
|
||
|
def gradient(scalar, frame):
|
||
|
"""
|
||
|
Returns the vector gradient of a scalar field computed wrt the
|
||
|
coordinate symbols of the given frame.
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
scalar : sympifiable
|
||
|
The scalar field to take the gradient of
|
||
|
|
||
|
frame : ReferenceFrame
|
||
|
The frame to calculate the gradient in
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.physics.vector import ReferenceFrame
|
||
|
>>> from sympy.physics.vector import gradient
|
||
|
>>> R = ReferenceFrame('R')
|
||
|
>>> s1 = R[0]*R[1]*R[2]
|
||
|
>>> gradient(s1, R)
|
||
|
R_y*R_z*R.x + R_x*R_z*R.y + R_x*R_y*R.z
|
||
|
>>> s2 = 5*R[0]**2*R[2]
|
||
|
>>> gradient(s2, R)
|
||
|
10*R_x*R_z*R.x + 5*R_x**2*R.z
|
||
|
|
||
|
"""
|
||
|
|
||
|
_check_frame(frame)
|
||
|
outvec = Vector(0)
|
||
|
scalar = express(scalar, frame, variables=True)
|
||
|
for i, x in enumerate(frame):
|
||
|
outvec += diff(scalar, frame[i]) * x
|
||
|
return outvec
|
||
|
|
||
|
|
||
|
def is_conservative(field):
|
||
|
"""
|
||
|
Checks if a field is conservative.
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
field : Vector
|
||
|
The field to check for conservative property
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.physics.vector import ReferenceFrame
|
||
|
>>> from sympy.physics.vector import is_conservative
|
||
|
>>> R = ReferenceFrame('R')
|
||
|
>>> is_conservative(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z)
|
||
|
True
|
||
|
>>> is_conservative(R[2] * R.y)
|
||
|
False
|
||
|
|
||
|
"""
|
||
|
|
||
|
# Field is conservative irrespective of frame
|
||
|
# Take the first frame in the result of the separate method of Vector
|
||
|
if field == Vector(0):
|
||
|
return True
|
||
|
frame = list(field.separate())[0]
|
||
|
return curl(field, frame).simplify() == Vector(0)
|
||
|
|
||
|
|
||
|
def is_solenoidal(field):
|
||
|
"""
|
||
|
Checks if a field is solenoidal.
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
field : Vector
|
||
|
The field to check for solenoidal property
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.physics.vector import ReferenceFrame
|
||
|
>>> from sympy.physics.vector import is_solenoidal
|
||
|
>>> R = ReferenceFrame('R')
|
||
|
>>> is_solenoidal(R[1]*R[2]*R.x + R[0]*R[2]*R.y + R[0]*R[1]*R.z)
|
||
|
True
|
||
|
>>> is_solenoidal(R[1] * R.y)
|
||
|
False
|
||
|
|
||
|
"""
|
||
|
|
||
|
# Field is solenoidal irrespective of frame
|
||
|
# Take the first frame in the result of the separate method in Vector
|
||
|
if field == Vector(0):
|
||
|
return True
|
||
|
frame = list(field.separate())[0]
|
||
|
return divergence(field, frame).simplify() is S.Zero
|
||
|
|
||
|
|
||
|
def scalar_potential(field, frame):
|
||
|
"""
|
||
|
Returns the scalar potential function of a field in a given frame
|
||
|
(without the added integration constant).
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
field : Vector
|
||
|
The vector field whose scalar potential function is to be
|
||
|
calculated
|
||
|
|
||
|
frame : ReferenceFrame
|
||
|
The frame to do the calculation in
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.physics.vector import ReferenceFrame
|
||
|
>>> from sympy.physics.vector import scalar_potential, gradient
|
||
|
>>> R = ReferenceFrame('R')
|
||
|
>>> scalar_potential(R.z, R) == R[2]
|
||
|
True
|
||
|
>>> scalar_field = 2*R[0]**2*R[1]*R[2]
|
||
|
>>> grad_field = gradient(scalar_field, R)
|
||
|
>>> scalar_potential(grad_field, R)
|
||
|
2*R_x**2*R_y*R_z
|
||
|
|
||
|
"""
|
||
|
|
||
|
# Check whether field is conservative
|
||
|
if not is_conservative(field):
|
||
|
raise ValueError("Field is not conservative")
|
||
|
if field == Vector(0):
|
||
|
return S.Zero
|
||
|
# Express the field exntirely in frame
|
||
|
# Substitute coordinate variables also
|
||
|
_check_frame(frame)
|
||
|
field = express(field, frame, variables=True)
|
||
|
# Make a list of dimensions of the frame
|
||
|
dimensions = list(frame)
|
||
|
# Calculate scalar potential function
|
||
|
temp_function = integrate(field.dot(dimensions[0]), frame[0])
|
||
|
for i, dim in enumerate(dimensions[1:]):
|
||
|
partial_diff = diff(temp_function, frame[i + 1])
|
||
|
partial_diff = field.dot(dim) - partial_diff
|
||
|
temp_function += integrate(partial_diff, frame[i + 1])
|
||
|
return temp_function
|
||
|
|
||
|
|
||
|
def scalar_potential_difference(field, frame, point1, point2, origin):
|
||
|
"""
|
||
|
Returns the scalar potential difference between two points in a
|
||
|
certain frame, wrt a given field.
|
||
|
|
||
|
If a scalar field is provided, its values at the two points are
|
||
|
considered. If a conservative vector field is provided, the values
|
||
|
of its scalar potential function at the two points are used.
|
||
|
|
||
|
Returns (potential at position 2) - (potential at position 1)
|
||
|
|
||
|
Parameters
|
||
|
==========
|
||
|
|
||
|
field : Vector/sympyfiable
|
||
|
The field to calculate wrt
|
||
|
|
||
|
frame : ReferenceFrame
|
||
|
The frame to do the calculations in
|
||
|
|
||
|
point1 : Point
|
||
|
The initial Point in given frame
|
||
|
|
||
|
position2 : Point
|
||
|
The second Point in the given frame
|
||
|
|
||
|
origin : Point
|
||
|
The Point to use as reference point for position vector
|
||
|
calculation
|
||
|
|
||
|
Examples
|
||
|
========
|
||
|
|
||
|
>>> from sympy.physics.vector import ReferenceFrame, Point
|
||
|
>>> from sympy.physics.vector import scalar_potential_difference
|
||
|
>>> R = ReferenceFrame('R')
|
||
|
>>> O = Point('O')
|
||
|
>>> P = O.locatenew('P', R[0]*R.x + R[1]*R.y + R[2]*R.z)
|
||
|
>>> vectfield = 4*R[0]*R[1]*R.x + 2*R[0]**2*R.y
|
||
|
>>> scalar_potential_difference(vectfield, R, O, P, O)
|
||
|
2*R_x**2*R_y
|
||
|
>>> Q = O.locatenew('O', 3*R.x + R.y + 2*R.z)
|
||
|
>>> scalar_potential_difference(vectfield, R, P, Q, O)
|
||
|
-2*R_x**2*R_y + 18
|
||
|
|
||
|
"""
|
||
|
|
||
|
_check_frame(frame)
|
||
|
if isinstance(field, Vector):
|
||
|
# Get the scalar potential function
|
||
|
scalar_fn = scalar_potential(field, frame)
|
||
|
else:
|
||
|
# Field is a scalar
|
||
|
scalar_fn = field
|
||
|
# Express positions in required frame
|
||
|
position1 = express(point1.pos_from(origin), frame, variables=True)
|
||
|
position2 = express(point2.pos_from(origin), frame, variables=True)
|
||
|
# Get the two positions as substitution dicts for coordinate variables
|
||
|
subs_dict1 = {}
|
||
|
subs_dict2 = {}
|
||
|
for i, x in enumerate(frame):
|
||
|
subs_dict1[frame[i]] = x.dot(position1)
|
||
|
subs_dict2[frame[i]] = x.dot(position2)
|
||
|
return scalar_fn.subs(subs_dict2) - scalar_fn.subs(subs_dict1)
|