m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

521 lines
15 KiB

6 months ago
  1. from sympy.vector.coordsysrect import CoordSys3D
  2. from sympy.vector.deloperator import Del
  3. from sympy.vector.scalar import BaseScalar
  4. from sympy.vector.vector import Vector, BaseVector
  5. from sympy.vector.operators import gradient, curl, divergence
  6. from sympy.core.function import diff
  7. from sympy.core.singleton import S
  8. from sympy.integrals.integrals import integrate
  9. from sympy.simplify.simplify import simplify
  10. from sympy.core import sympify
  11. from sympy.vector.dyadic import Dyadic
  12. def express(expr, system, system2=None, variables=False):
  13. """
  14. Global function for 'express' functionality.
  15. Re-expresses a Vector, Dyadic or scalar(sympyfiable) in the given
  16. coordinate system.
  17. If 'variables' is True, then the coordinate variables (base scalars)
  18. of other coordinate systems present in the vector/scalar field or
  19. dyadic are also substituted in terms of the base scalars of the
  20. given system.
  21. Parameters
  22. ==========
  23. expr : Vector/Dyadic/scalar(sympyfiable)
  24. The expression to re-express in CoordSys3D 'system'
  25. system: CoordSys3D
  26. The coordinate system the expr is to be expressed in
  27. system2: CoordSys3D
  28. The other coordinate system required for re-expression
  29. (only for a Dyadic Expr)
  30. variables : boolean
  31. Specifies whether to substitute the coordinate variables present
  32. in expr, in terms of those of parameter system
  33. Examples
  34. ========
  35. >>> from sympy.vector import CoordSys3D
  36. >>> from sympy import Symbol, cos, sin
  37. >>> N = CoordSys3D('N')
  38. >>> q = Symbol('q')
  39. >>> B = N.orient_new_axis('B', q, N.k)
  40. >>> from sympy.vector import express
  41. >>> express(B.i, N)
  42. (cos(q))*N.i + (sin(q))*N.j
  43. >>> express(N.x, B, variables=True)
  44. B.x*cos(q) - B.y*sin(q)
  45. >>> d = N.i.outer(N.i)
  46. >>> express(d, B, N) == (cos(q))*(B.i|N.i) + (-sin(q))*(B.j|N.i)
  47. True
  48. """
  49. if expr in (0, Vector.zero):
  50. return expr
  51. if not isinstance(system, CoordSys3D):
  52. raise TypeError("system should be a CoordSys3D \
  53. instance")
  54. if isinstance(expr, Vector):
  55. if system2 is not None:
  56. raise ValueError("system2 should not be provided for \
  57. Vectors")
  58. # Given expr is a Vector
  59. if variables:
  60. # If variables attribute is True, substitute
  61. # the coordinate variables in the Vector
  62. system_list = []
  63. for x in expr.atoms(BaseScalar, BaseVector):
  64. if x.system != system:
  65. system_list.append(x.system)
  66. system_list = set(system_list)
  67. subs_dict = {}
  68. for f in system_list:
  69. subs_dict.update(f.scalar_map(system))
  70. expr = expr.subs(subs_dict)
  71. # Re-express in this coordinate system
  72. outvec = Vector.zero
  73. parts = expr.separate()
  74. for x in parts:
  75. if x != system:
  76. temp = system.rotation_matrix(x) * parts[x].to_matrix(x)
  77. outvec += matrix_to_vector(temp, system)
  78. else:
  79. outvec += parts[x]
  80. return outvec
  81. elif isinstance(expr, Dyadic):
  82. if system2 is None:
  83. system2 = system
  84. if not isinstance(system2, CoordSys3D):
  85. raise TypeError("system2 should be a CoordSys3D \
  86. instance")
  87. outdyad = Dyadic.zero
  88. var = variables
  89. for k, v in expr.components.items():
  90. outdyad += (express(v, system, variables=var) *
  91. (express(k.args[0], system, variables=var) |
  92. express(k.args[1], system2, variables=var)))
  93. return outdyad
  94. else:
  95. if system2 is not None:
  96. raise ValueError("system2 should not be provided for \
  97. Vectors")
  98. if variables:
  99. # Given expr is a scalar field
  100. system_set = set()
  101. expr = sympify(expr)
  102. # Substitute all the coordinate variables
  103. for x in expr.atoms(BaseScalar):
  104. if x.system != system:
  105. system_set.add(x.system)
  106. subs_dict = {}
  107. for f in system_set:
  108. subs_dict.update(f.scalar_map(system))
  109. return expr.subs(subs_dict)
  110. return expr
  111. def directional_derivative(field, direction_vector):
  112. """
  113. Returns the directional derivative of a scalar or vector field computed
  114. along a given vector in coordinate system which parameters are expressed.
  115. Parameters
  116. ==========
  117. field : Vector or Scalar
  118. The scalar or vector field to compute the directional derivative of
  119. direction_vector : Vector
  120. The vector to calculated directional derivative along them.
  121. Examples
  122. ========
  123. >>> from sympy.vector import CoordSys3D, directional_derivative
  124. >>> R = CoordSys3D('R')
  125. >>> f1 = R.x*R.y*R.z
  126. >>> v1 = 3*R.i + 4*R.j + R.k
  127. >>> directional_derivative(f1, v1)
  128. R.x*R.y + 4*R.x*R.z + 3*R.y*R.z
  129. >>> f2 = 5*R.x**2*R.z
  130. >>> directional_derivative(f2, v1)
  131. 5*R.x**2 + 30*R.x*R.z
  132. """
  133. from sympy.vector.operators import _get_coord_systems
  134. coord_sys = _get_coord_systems(field)
  135. if len(coord_sys) > 0:
  136. # TODO: This gets a random coordinate system in case of multiple ones:
  137. coord_sys = next(iter(coord_sys))
  138. field = express(field, coord_sys, variables=True)
  139. i, j, k = coord_sys.base_vectors()
  140. x, y, z = coord_sys.base_scalars()
  141. out = Vector.dot(direction_vector, i) * diff(field, x)
  142. out += Vector.dot(direction_vector, j) * diff(field, y)
  143. out += Vector.dot(direction_vector, k) * diff(field, z)
  144. if out == 0 and isinstance(field, Vector):
  145. out = Vector.zero
  146. return out
  147. elif isinstance(field, Vector):
  148. return Vector.zero
  149. else:
  150. return S.Zero
  151. def laplacian(expr):
  152. """
  153. Return the laplacian of the given field computed in terms of
  154. the base scalars of the given coordinate system.
  155. Parameters
  156. ==========
  157. expr : SymPy Expr or Vector
  158. expr denotes a scalar or vector field.
  159. Examples
  160. ========
  161. >>> from sympy.vector import CoordSys3D, laplacian
  162. >>> R = CoordSys3D('R')
  163. >>> f = R.x**2*R.y**5*R.z
  164. >>> laplacian(f)
  165. 20*R.x**2*R.y**3*R.z + 2*R.y**5*R.z
  166. >>> f = R.x**2*R.i + R.y**3*R.j + R.z**4*R.k
  167. >>> laplacian(f)
  168. 2*R.i + 6*R.y*R.j + 12*R.z**2*R.k
  169. """
  170. delop = Del()
  171. if expr.is_Vector:
  172. return (gradient(divergence(expr)) - curl(curl(expr))).doit()
  173. return delop.dot(delop(expr)).doit()
  174. def is_conservative(field):
  175. """
  176. Checks if a field is conservative.
  177. Parameters
  178. ==========
  179. field : Vector
  180. The field to check for conservative property
  181. Examples
  182. ========
  183. >>> from sympy.vector import CoordSys3D
  184. >>> from sympy.vector import is_conservative
  185. >>> R = CoordSys3D('R')
  186. >>> is_conservative(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k)
  187. True
  188. >>> is_conservative(R.z*R.j)
  189. False
  190. """
  191. # Field is conservative irrespective of system
  192. # Take the first coordinate system in the result of the
  193. # separate method of Vector
  194. if not isinstance(field, Vector):
  195. raise TypeError("field should be a Vector")
  196. if field == Vector.zero:
  197. return True
  198. return curl(field).simplify() == Vector.zero
  199. def is_solenoidal(field):
  200. """
  201. Checks if a field is solenoidal.
  202. Parameters
  203. ==========
  204. field : Vector
  205. The field to check for solenoidal property
  206. Examples
  207. ========
  208. >>> from sympy.vector import CoordSys3D
  209. >>> from sympy.vector import is_solenoidal
  210. >>> R = CoordSys3D('R')
  211. >>> is_solenoidal(R.y*R.z*R.i + R.x*R.z*R.j + R.x*R.y*R.k)
  212. True
  213. >>> is_solenoidal(R.y * R.j)
  214. False
  215. """
  216. # Field is solenoidal irrespective of system
  217. # Take the first coordinate system in the result of the
  218. # separate method in Vector
  219. if not isinstance(field, Vector):
  220. raise TypeError("field should be a Vector")
  221. if field == Vector.zero:
  222. return True
  223. return divergence(field).simplify() is S.Zero
  224. def scalar_potential(field, coord_sys):
  225. """
  226. Returns the scalar potential function of a field in a given
  227. coordinate system (without the added integration constant).
  228. Parameters
  229. ==========
  230. field : Vector
  231. The vector field whose scalar potential function is to be
  232. calculated
  233. coord_sys : CoordSys3D
  234. The coordinate system to do the calculation in
  235. Examples
  236. ========
  237. >>> from sympy.vector import CoordSys3D
  238. >>> from sympy.vector import scalar_potential, gradient
  239. >>> R = CoordSys3D('R')
  240. >>> scalar_potential(R.k, R) == R.z
  241. True
  242. >>> scalar_field = 2*R.x**2*R.y*R.z
  243. >>> grad_field = gradient(scalar_field)
  244. >>> scalar_potential(grad_field, R)
  245. 2*R.x**2*R.y*R.z
  246. """
  247. # Check whether field is conservative
  248. if not is_conservative(field):
  249. raise ValueError("Field is not conservative")
  250. if field == Vector.zero:
  251. return S.Zero
  252. # Express the field exntirely in coord_sys
  253. # Substitute coordinate variables also
  254. if not isinstance(coord_sys, CoordSys3D):
  255. raise TypeError("coord_sys must be a CoordSys3D")
  256. field = express(field, coord_sys, variables=True)
  257. dimensions = coord_sys.base_vectors()
  258. scalars = coord_sys.base_scalars()
  259. # Calculate scalar potential function
  260. temp_function = integrate(field.dot(dimensions[0]), scalars[0])
  261. for i, dim in enumerate(dimensions[1:]):
  262. partial_diff = diff(temp_function, scalars[i + 1])
  263. partial_diff = field.dot(dim) - partial_diff
  264. temp_function += integrate(partial_diff, scalars[i + 1])
  265. return temp_function
  266. def scalar_potential_difference(field, coord_sys, point1, point2):
  267. """
  268. Returns the scalar potential difference between two points in a
  269. certain coordinate system, wrt a given field.
  270. If a scalar field is provided, its values at the two points are
  271. considered. If a conservative vector field is provided, the values
  272. of its scalar potential function at the two points are used.
  273. Returns (potential at point2) - (potential at point1)
  274. The position vectors of the two Points are calculated wrt the
  275. origin of the coordinate system provided.
  276. Parameters
  277. ==========
  278. field : Vector/Expr
  279. The field to calculate wrt
  280. coord_sys : CoordSys3D
  281. The coordinate system to do the calculations in
  282. point1 : Point
  283. The initial Point in given coordinate system
  284. position2 : Point
  285. The second Point in the given coordinate system
  286. Examples
  287. ========
  288. >>> from sympy.vector import CoordSys3D
  289. >>> from sympy.vector import scalar_potential_difference
  290. >>> R = CoordSys3D('R')
  291. >>> P = R.origin.locate_new('P', R.x*R.i + R.y*R.j + R.z*R.k)
  292. >>> vectfield = 4*R.x*R.y*R.i + 2*R.x**2*R.j
  293. >>> scalar_potential_difference(vectfield, R, R.origin, P)
  294. 2*R.x**2*R.y
  295. >>> Q = R.origin.locate_new('O', 3*R.i + R.j + 2*R.k)
  296. >>> scalar_potential_difference(vectfield, R, P, Q)
  297. -2*R.x**2*R.y + 18
  298. """
  299. if not isinstance(coord_sys, CoordSys3D):
  300. raise TypeError("coord_sys must be a CoordSys3D")
  301. if isinstance(field, Vector):
  302. # Get the scalar potential function
  303. scalar_fn = scalar_potential(field, coord_sys)
  304. else:
  305. # Field is a scalar
  306. scalar_fn = field
  307. # Express positions in required coordinate system
  308. origin = coord_sys.origin
  309. position1 = express(point1.position_wrt(origin), coord_sys,
  310. variables=True)
  311. position2 = express(point2.position_wrt(origin), coord_sys,
  312. variables=True)
  313. # Get the two positions as substitution dicts for coordinate variables
  314. subs_dict1 = {}
  315. subs_dict2 = {}
  316. scalars = coord_sys.base_scalars()
  317. for i, x in enumerate(coord_sys.base_vectors()):
  318. subs_dict1[scalars[i]] = x.dot(position1)
  319. subs_dict2[scalars[i]] = x.dot(position2)
  320. return scalar_fn.subs(subs_dict2) - scalar_fn.subs(subs_dict1)
  321. def matrix_to_vector(matrix, system):
  322. """
  323. Converts a vector in matrix form to a Vector instance.
  324. It is assumed that the elements of the Matrix represent the
  325. measure numbers of the components of the vector along basis
  326. vectors of 'system'.
  327. Parameters
  328. ==========
  329. matrix : SymPy Matrix, Dimensions: (3, 1)
  330. The matrix to be converted to a vector
  331. system : CoordSys3D
  332. The coordinate system the vector is to be defined in
  333. Examples
  334. ========
  335. >>> from sympy import ImmutableMatrix as Matrix
  336. >>> m = Matrix([1, 2, 3])
  337. >>> from sympy.vector import CoordSys3D, matrix_to_vector
  338. >>> C = CoordSys3D('C')
  339. >>> v = matrix_to_vector(m, C)
  340. >>> v
  341. C.i + 2*C.j + 3*C.k
  342. >>> v.to_matrix(C) == m
  343. True
  344. """
  345. outvec = Vector.zero
  346. vects = system.base_vectors()
  347. for i, x in enumerate(matrix):
  348. outvec += x * vects[i]
  349. return outvec
  350. def _path(from_object, to_object):
  351. """
  352. Calculates the 'path' of objects starting from 'from_object'
  353. to 'to_object', along with the index of the first common
  354. ancestor in the tree.
  355. Returns (index, list) tuple.
  356. """
  357. if from_object._root != to_object._root:
  358. raise ValueError("No connecting path found between " +
  359. str(from_object) + " and " + str(to_object))
  360. other_path = []
  361. obj = to_object
  362. while obj._parent is not None:
  363. other_path.append(obj)
  364. obj = obj._parent
  365. other_path.append(obj)
  366. object_set = set(other_path)
  367. from_path = []
  368. obj = from_object
  369. while obj not in object_set:
  370. from_path.append(obj)
  371. obj = obj._parent
  372. index = len(from_path)
  373. i = other_path.index(obj)
  374. while i >= 0:
  375. from_path.append(other_path[i])
  376. i -= 1
  377. return index, from_path
  378. def orthogonalize(*vlist, orthonormal=False):
  379. """
  380. Takes a sequence of independent vectors and orthogonalizes them
  381. using the Gram - Schmidt process. Returns a list of
  382. orthogonal or orthonormal vectors.
  383. Parameters
  384. ==========
  385. vlist : sequence of independent vectors to be made orthogonal.
  386. orthonormal : Optional parameter
  387. Set to True if the vectors returned should be
  388. orthonormal.
  389. Default: False
  390. Examples
  391. ========
  392. >>> from sympy.vector.coordsysrect import CoordSys3D
  393. >>> from sympy.vector.functions import orthogonalize
  394. >>> C = CoordSys3D('C')
  395. >>> i, j, k = C.base_vectors()
  396. >>> v1 = i + 2*j
  397. >>> v2 = 2*i + 3*j
  398. >>> orthogonalize(v1, v2)
  399. [C.i + 2*C.j, 2/5*C.i + (-1/5)*C.j]
  400. References
  401. ==========
  402. .. [1] https://en.wikipedia.org/wiki/Gram-Schmidt_process
  403. """
  404. if not all(isinstance(vec, Vector) for vec in vlist):
  405. raise TypeError('Each element must be of Type Vector')
  406. ortho_vlist = []
  407. for i, term in enumerate(vlist):
  408. for j in range(i):
  409. term -= ortho_vlist[j].projection(vlist[i])
  410. # TODO : The following line introduces a performance issue
  411. # and needs to be changed once a good solution for issue #10279 is
  412. # found.
  413. if simplify(term).equals(Vector.zero):
  414. raise ValueError("Vector set not linearly independent")
  415. ortho_vlist.append(term)
  416. if orthonormal:
  417. ortho_vlist = [vec.normalize() for vec in ortho_vlist]
  418. return ortho_vlist