123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- from sympy.vector.coordsysrect import CoordSys3D
- from sympy.vector.deloperator import Del
- from sympy.vector.scalar import BaseScalar
- from sympy.vector.vector import Vector, BaseVector
- from sympy.vector.operators import gradient, curl, divergence
- from sympy.core.function import diff
- from sympy.core.singleton import S
- from sympy.integrals.integrals import integrate
- from sympy.simplify.simplify import simplify
- from sympy.core import sympify
- from sympy.vector.dyadic import Dyadic
- def express(expr, system, system2=None, variables=False):
- """
- Global function for 'express' functionality.
- Re-expresses a Vector, Dyadic or scalar(sympyfiable) in the given
- coordinate system.
- If 'variables' is True, then the coordinate variables (base scalars)
- of other coordinate systems present in the vector/scalar field or
- dyadic are also substituted in terms of the base scalars of the
- given system.
- Parameters
- ==========
- expr : Vector/Dyadic/scalar(sympyfiable)
- The expression to re-express in CoordSys3D 'system'
- system: CoordSys3D
- The coordinate system the expr is to be expressed in
- system2: CoordSys3D
- The other coordinate system required for re-expression
- (only for a Dyadic Expr)
- variables : boolean
- Specifies whether to substitute the coordinate variables present
- in expr, in terms of those of parameter system
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> from sympy import Symbol, cos, sin
- >>> N = CoordSys3D('N')
- >>> q = Symbol('q')
- >>> B = N.orient_new_axis('B', q, N.k)
- >>> from sympy.vector import express
- >>> express(B.i, N)
- (cos(q))*N.i + (sin(q))*N.j
- >>> express(N.x, B, variables=True)
- B.x*cos(q) - B.y*sin(q)
- >>> d = N.i.outer(N.i)
- >>> express(d, B, N) == (cos(q))*(B.i|N.i) + (-sin(q))*(B.j|N.i)
- True
- """
- if expr in (0, Vector.zero):
- return expr
- if not isinstance(system, CoordSys3D):
- raise TypeError("system should be a CoordSys3D \
- instance")
- if isinstance(expr, Vector):
- if system2 is not None:
- raise ValueError("system2 should not be provided for \
- Vectors")
- # Given expr is a Vector
- if variables:
- # If variables attribute is True, substitute
- # the coordinate variables in the Vector
- system_list = {x.system for x in expr.atoms(BaseScalar, BaseVector)} - {system}
- subs_dict = {}
- for f in system_list:
- subs_dict.update(f.scalar_map(system))
- expr = expr.subs(subs_dict)
- # Re-express in this coordinate system
- outvec = Vector.zero
- parts = expr.separate()
- for x in parts:
- if x != system:
- temp = system.rotation_matrix(x) * parts[x].to_matrix(x)
- outvec += matrix_to_vector(temp, system)
- else:
- outvec += parts[x]
- return outvec
- elif isinstance(expr, Dyadic):
- if system2 is None:
- system2 = system
- if not isinstance(system2, CoordSys3D):
- raise TypeError("system2 should be a CoordSys3D \
- instance")
- outdyad = Dyadic.zero
- var = variables
- for k, v in expr.components.items():
- outdyad += (express(v, system, variables=var) *
- (express(k.args[0], system, variables=var) |
- express(k.args[1], system2, variables=var)))
- return outdyad
- else:
- if system2 is not None:
- raise ValueError("system2 should not be provided for \
- Vectors")
- if variables:
- # Given expr is a scalar field
- system_set = set()
- expr = sympify(expr)
- # Substitute all the coordinate variables
- for x in expr.atoms(BaseScalar):
- if x.system != system:
- system_set.add(x.system)
- subs_dict = {}
- for f in system_set:
- subs_dict.update(f.scalar_map(system))
- return expr.subs(subs_dict)
- return expr
- def directional_derivative(field, direction_vector):
- """
- Returns the directional derivative of a scalar or vector field computed
- along a given vector in coordinate system which parameters are expressed.
- Parameters
- ==========
- field : Vector or Scalar
- The scalar or vector field to compute the directional derivative of
- direction_vector : Vector
- The vector to calculated directional derivative along them.
- Examples
- ========
- >>> from sympy.vector import CoordSys3D, directional_derivative
- >>> R = CoordSys3D('R')
- >>> f1 = R.x*R.y*R.z
- >>> v1 = 3*R.i + 4*R.j + R.k
- >>> directional_derivative(f1, v1)
- R.x*R.y + 4*R.x*R.z + 3*R.y*R.z
- >>> f2 = 5*R.x**2*R.z
- >>> directional_derivative(f2, v1)
- 5*R.x**2 + 30*R.x*R.z
- """
- from sympy.vector.operators import _get_coord_systems
- coord_sys = _get_coord_systems(field)
- if len(coord_sys) > 0:
- # TODO: This gets a random coordinate system in case of multiple ones:
- coord_sys = next(iter(coord_sys))
- field = express(field, coord_sys, variables=True)
- i, j, k = coord_sys.base_vectors()
- x, y, z = coord_sys.base_scalars()
- out = Vector.dot(direction_vector, i) * diff(field, x)
- out += Vector.dot(direction_vector, j) * diff(field, y)
- out += Vector.dot(direction_vector, k) * diff(field, z)
- if out == 0 and isinstance(field, Vector):
- out = Vector.zero
- return out
- elif isinstance(field, Vector):
- return Vector.zero
- else:
- return S.Zero
- def laplacian(expr):
- """
- Return the laplacian of the given field computed in terms of
- the base scalars of the given coordinate system.
- Parameters
- ==========
- expr : SymPy Expr or Vector
- expr denotes a scalar or vector field.
- Examples
- ========
- >>> from sympy.vector import CoordSys3D, laplacian
- >>> R = CoordSys3D('R')
- >>> f = R.x**2*R.y**5*R.z
- >>> laplacian(f)
- 20*R.x**2*R.y**3*R.z + 2*R.y**5*R.z
- >>> f = R.x**2*R.i + R.y**3*R.j + R.z**4*R.k
- >>> laplacian(f)
- 2*R.i + 6*R.y*R.j + 12*R.z**2*R.k
- """
- delop = Del()
- if expr.is_Vector:
- return (gradient(divergence(expr)) - curl(curl(expr))).doit()
- return delop.dot(delop(expr)).doit()
- def is_conservative(field):
- """
- Checks if a field is conservative.
- Parameters
- ==========
- field : Vector
- The field to check for conservative property
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> from sympy.vector import is_conservative
- >>> R = CoordSys3D('R')
- >>> is_conservative(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k)
- True
- >>> is_conservative(R.z*R.j)
- False
- """
- # Field is conservative irrespective of system
- # Take the first coordinate system in the result of the
- # separate method of Vector
- if not isinstance(field, Vector):
- raise TypeError("field should be a Vector")
- if field == Vector.zero:
- return True
- return curl(field).simplify() == Vector.zero
- def is_solenoidal(field):
- """
- Checks if a field is solenoidal.
- Parameters
- ==========
- field : Vector
- The field to check for solenoidal property
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> from sympy.vector import is_solenoidal
- >>> R = CoordSys3D('R')
- >>> is_solenoidal(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k)
- True
- >>> is_solenoidal(R.y * R.j)
- False
- """
- # Field is solenoidal irrespective of system
- # Take the first coordinate system in the result of the
- # separate method in Vector
- if not isinstance(field, Vector):
- raise TypeError("field should be a Vector")
- if field == Vector.zero:
- return True
- return divergence(field).simplify() is S.Zero
- def scalar_potential(field, coord_sys):
- """
- Returns the scalar potential function of a field in a given
- coordinate system (without the added integration constant).
- Parameters
- ==========
- field : Vector
- The vector field whose scalar potential function is to be
- calculated
- coord_sys : CoordSys3D
- The coordinate system to do the calculation in
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> from sympy.vector import scalar_potential, gradient
- >>> R = CoordSys3D('R')
- >>> scalar_potential(R.k, R) == R.z
- True
- >>> scalar_field = 2*R.x**2*R.y*R.z
- >>> grad_field = gradient(scalar_field)
- >>> 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.zero:
- return S.Zero
- # Express the field exntirely in coord_sys
- # Substitute coordinate variables also
- if not isinstance(coord_sys, CoordSys3D):
- raise TypeError("coord_sys must be a CoordSys3D")
- field = express(field, coord_sys, variables=True)
- dimensions = coord_sys.base_vectors()
- scalars = coord_sys.base_scalars()
- # Calculate scalar potential function
- temp_function = integrate(field.dot(dimensions[0]), scalars[0])
- for i, dim in enumerate(dimensions[1:]):
- partial_diff = diff(temp_function, scalars[i + 1])
- partial_diff = field.dot(dim) - partial_diff
- temp_function += integrate(partial_diff, scalars[i + 1])
- return temp_function
- def scalar_potential_difference(field, coord_sys, point1, point2):
- """
- Returns the scalar potential difference between two points in a
- certain coordinate system, 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 point2) - (potential at point1)
- The position vectors of the two Points are calculated wrt the
- origin of the coordinate system provided.
- Parameters
- ==========
- field : Vector/Expr
- The field to calculate wrt
- coord_sys : CoordSys3D
- The coordinate system to do the calculations in
- point1 : Point
- The initial Point in given coordinate system
- position2 : Point
- The second Point in the given coordinate system
- Examples
- ========
- >>> from sympy.vector import CoordSys3D
- >>> from sympy.vector import scalar_potential_difference
- >>> R = CoordSys3D('R')
- >>> P = R.origin.locate_new('P', R.x*R.i + R.y*R.j + R.z*R.k)
- >>> vectfield = 4*R.x*R.y*R.i + 2*R.x**2*R.j
- >>> scalar_potential_difference(vectfield, R, R.origin, P)
- 2*R.x**2*R.y
- >>> Q = R.origin.locate_new('O', 3*R.i + R.j + 2*R.k)
- >>> scalar_potential_difference(vectfield, R, P, Q)
- -2*R.x**2*R.y + 18
- """
- if not isinstance(coord_sys, CoordSys3D):
- raise TypeError("coord_sys must be a CoordSys3D")
- if isinstance(field, Vector):
- # Get the scalar potential function
- scalar_fn = scalar_potential(field, coord_sys)
- else:
- # Field is a scalar
- scalar_fn = field
- # Express positions in required coordinate system
- origin = coord_sys.origin
- position1 = express(point1.position_wrt(origin), coord_sys,
- variables=True)
- position2 = express(point2.position_wrt(origin), coord_sys,
- variables=True)
- # Get the two positions as substitution dicts for coordinate variables
- subs_dict1 = {}
- subs_dict2 = {}
- scalars = coord_sys.base_scalars()
- for i, x in enumerate(coord_sys.base_vectors()):
- subs_dict1[scalars[i]] = x.dot(position1)
- subs_dict2[scalars[i]] = x.dot(position2)
- return scalar_fn.subs(subs_dict2) - scalar_fn.subs(subs_dict1)
- def matrix_to_vector(matrix, system):
- """
- Converts a vector in matrix form to a Vector instance.
- It is assumed that the elements of the Matrix represent the
- measure numbers of the components of the vector along basis
- vectors of 'system'.
- Parameters
- ==========
- matrix : SymPy Matrix, Dimensions: (3, 1)
- The matrix to be converted to a vector
- system : CoordSys3D
- The coordinate system the vector is to be defined in
- Examples
- ========
- >>> from sympy import ImmutableMatrix as Matrix
- >>> m = Matrix([1, 2, 3])
- >>> from sympy.vector import CoordSys3D, matrix_to_vector
- >>> C = CoordSys3D('C')
- >>> v = matrix_to_vector(m, C)
- >>> v
- C.i + 2*C.j + 3*C.k
- >>> v.to_matrix(C) == m
- True
- """
- outvec = Vector.zero
- vects = system.base_vectors()
- for i, x in enumerate(matrix):
- outvec += x * vects[i]
- return outvec
- def _path(from_object, to_object):
- """
- Calculates the 'path' of objects starting from 'from_object'
- to 'to_object', along with the index of the first common
- ancestor in the tree.
- Returns (index, list) tuple.
- """
- if from_object._root != to_object._root:
- raise ValueError("No connecting path found between " +
- str(from_object) + " and " + str(to_object))
- other_path = []
- obj = to_object
- while obj._parent is not None:
- other_path.append(obj)
- obj = obj._parent
- other_path.append(obj)
- object_set = set(other_path)
- from_path = []
- obj = from_object
- while obj not in object_set:
- from_path.append(obj)
- obj = obj._parent
- index = len(from_path)
- i = other_path.index(obj)
- while i >= 0:
- from_path.append(other_path[i])
- i -= 1
- return index, from_path
- def orthogonalize(*vlist, orthonormal=False):
- """
- Takes a sequence of independent vectors and orthogonalizes them
- using the Gram - Schmidt process. Returns a list of
- orthogonal or orthonormal vectors.
- Parameters
- ==========
- vlist : sequence of independent vectors to be made orthogonal.
- orthonormal : Optional parameter
- Set to True if the vectors returned should be
- orthonormal.
- Default: False
- Examples
- ========
- >>> from sympy.vector.coordsysrect import CoordSys3D
- >>> from sympy.vector.functions import orthogonalize
- >>> C = CoordSys3D('C')
- >>> i, j, k = C.base_vectors()
- >>> v1 = i + 2*j
- >>> v2 = 2*i + 3*j
- >>> orthogonalize(v1, v2)
- [C.i + 2*C.j, 2/5*C.i + (-1/5)*C.j]
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Gram-Schmidt_process
- """
- if not all(isinstance(vec, Vector) for vec in vlist):
- raise TypeError('Each element must be of Type Vector')
- ortho_vlist = []
- for i, term in enumerate(vlist):
- for j in range(i):
- term -= ortho_vlist[j].projection(vlist[i])
- # TODO : The following line introduces a performance issue
- # and needs to be changed once a good solution for issue #10279 is
- # found.
- if simplify(term).equals(Vector.zero):
- raise ValueError("Vector set not linearly independent")
- ortho_vlist.append(term)
- if orthonormal:
- ortho_vlist = [vec.normalize() for vec in ortho_vlist]
- return ortho_vlist
|