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)