integrals.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. from sympy.core import Basic, diff
  2. from sympy.core.singleton import S
  3. from sympy.core.sorting import default_sort_key
  4. from sympy.matrices import Matrix
  5. from sympy.integrals import Integral, integrate
  6. from sympy.geometry.entity import GeometryEntity
  7. from sympy.simplify.simplify import simplify
  8. from sympy.utilities.iterables import topological_sort
  9. from sympy.vector import (CoordSys3D, Vector, ParametricRegion,
  10. parametric_region_list, ImplicitRegion)
  11. from sympy.vector.operators import _get_coord_systems
  12. class ParametricIntegral(Basic):
  13. """
  14. Represents integral of a scalar or vector field
  15. over a Parametric Region
  16. Examples
  17. ========
  18. >>> from sympy import cos, sin, pi
  19. >>> from sympy.vector import CoordSys3D, ParametricRegion, ParametricIntegral
  20. >>> from sympy.abc import r, t, theta, phi
  21. >>> C = CoordSys3D('C')
  22. >>> curve = ParametricRegion((3*t - 2, t + 1), (t, 1, 2))
  23. >>> ParametricIntegral(C.x, curve)
  24. 5*sqrt(10)/2
  25. >>> length = ParametricIntegral(1, curve)
  26. >>> length
  27. sqrt(10)
  28. >>> semisphere = ParametricRegion((2*sin(phi)*cos(theta), 2*sin(phi)*sin(theta), 2*cos(phi)),\
  29. (theta, 0, 2*pi), (phi, 0, pi/2))
  30. >>> ParametricIntegral(C.z, semisphere)
  31. 8*pi
  32. >>> ParametricIntegral(C.j + C.k, ParametricRegion((r*cos(theta), r*sin(theta)), r, theta))
  33. 0
  34. """
  35. def __new__(cls, field, parametricregion):
  36. coord_set = _get_coord_systems(field)
  37. if len(coord_set) == 0:
  38. coord_sys = CoordSys3D('C')
  39. elif len(coord_set) > 1:
  40. raise ValueError
  41. else:
  42. coord_sys = next(iter(coord_set))
  43. if parametricregion.dimensions == 0:
  44. return S.Zero
  45. base_vectors = coord_sys.base_vectors()
  46. base_scalars = coord_sys.base_scalars()
  47. parametricfield = field
  48. r = Vector.zero
  49. for i in range(len(parametricregion.definition)):
  50. r += base_vectors[i]*parametricregion.definition[i]
  51. if len(coord_set) != 0:
  52. for i in range(len(parametricregion.definition)):
  53. parametricfield = parametricfield.subs(base_scalars[i], parametricregion.definition[i])
  54. if parametricregion.dimensions == 1:
  55. parameter = parametricregion.parameters[0]
  56. r_diff = diff(r, parameter)
  57. lower, upper = parametricregion.limits[parameter][0], parametricregion.limits[parameter][1]
  58. if isinstance(parametricfield, Vector):
  59. integrand = simplify(r_diff.dot(parametricfield))
  60. else:
  61. integrand = simplify(r_diff.magnitude()*parametricfield)
  62. result = integrate(integrand, (parameter, lower, upper))
  63. elif parametricregion.dimensions == 2:
  64. u, v = cls._bounds_case(parametricregion.parameters, parametricregion.limits)
  65. r_u = diff(r, u)
  66. r_v = diff(r, v)
  67. normal_vector = simplify(r_u.cross(r_v))
  68. if isinstance(parametricfield, Vector):
  69. integrand = parametricfield.dot(normal_vector)
  70. else:
  71. integrand = parametricfield*normal_vector.magnitude()
  72. integrand = simplify(integrand)
  73. lower_u, upper_u = parametricregion.limits[u][0], parametricregion.limits[u][1]
  74. lower_v, upper_v = parametricregion.limits[v][0], parametricregion.limits[v][1]
  75. result = integrate(integrand, (u, lower_u, upper_u), (v, lower_v, upper_v))
  76. else:
  77. variables = cls._bounds_case(parametricregion.parameters, parametricregion.limits)
  78. coeff = Matrix(parametricregion.definition).jacobian(variables).det()
  79. integrand = simplify(parametricfield*coeff)
  80. l = [(var, parametricregion.limits[var][0], parametricregion.limits[var][1]) for var in variables]
  81. result = integrate(integrand, *l)
  82. if not isinstance(result, Integral):
  83. return result
  84. else:
  85. return super().__new__(cls, field, parametricregion)
  86. @classmethod
  87. def _bounds_case(cls, parameters, limits):
  88. V = list(limits.keys())
  89. E = []
  90. for p in V:
  91. lower_p = limits[p][0]
  92. upper_p = limits[p][1]
  93. lower_p = lower_p.atoms()
  94. upper_p = upper_p.atoms()
  95. E.extend((p, q) for q in V if p != q and
  96. (lower_p.issuperset({q}) or upper_p.issuperset({q})))
  97. if not E:
  98. return parameters
  99. else:
  100. return topological_sort((V, E), key=default_sort_key)
  101. @property
  102. def field(self):
  103. return self.args[0]
  104. @property
  105. def parametricregion(self):
  106. return self.args[1]
  107. def vector_integrate(field, *region):
  108. """
  109. Compute the integral of a vector/scalar field
  110. over a a region or a set of parameters.
  111. Examples
  112. ========
  113. >>> from sympy.vector import CoordSys3D, ParametricRegion, vector_integrate
  114. >>> from sympy.abc import x, y, t
  115. >>> C = CoordSys3D('C')
  116. >>> region = ParametricRegion((t, t**2), (t, 1, 5))
  117. >>> vector_integrate(C.x*C.i, region)
  118. 12
  119. Integrals over some objects of geometry module can also be calculated.
  120. >>> from sympy.geometry import Point, Circle, Triangle
  121. >>> c = Circle(Point(0, 2), 5)
  122. >>> vector_integrate(C.x**2 + C.y**2, c)
  123. 290*pi
  124. >>> triangle = Triangle(Point(-2, 3), Point(2, 3), Point(0, 5))
  125. >>> vector_integrate(3*C.x**2*C.y*C.i + C.j, triangle)
  126. -8
  127. Integrals over some simple implicit regions can be computed. But in most cases,
  128. it takes too long to compute over them. This is due to the expressions of parametric
  129. representation becoming large.
  130. >>> from sympy.vector import ImplicitRegion
  131. >>> c2 = ImplicitRegion((x, y), (x - 2)**2 + (y - 1)**2 - 9)
  132. >>> vector_integrate(1, c2)
  133. 6*pi
  134. Integral of fields with respect to base scalars:
  135. >>> vector_integrate(12*C.y**3, (C.y, 1, 3))
  136. 240
  137. >>> vector_integrate(C.x**2*C.z, C.x)
  138. C.x**3*C.z/3
  139. >>> vector_integrate(C.x*C.i - C.y*C.k, C.x)
  140. (Integral(C.x, C.x))*C.i + (Integral(-C.y, C.x))*C.k
  141. >>> _.doit()
  142. C.x**2/2*C.i + (-C.x*C.y)*C.k
  143. """
  144. if len(region) == 1:
  145. if isinstance(region[0], ParametricRegion):
  146. return ParametricIntegral(field, region[0])
  147. if isinstance(region[0], ImplicitRegion):
  148. region = parametric_region_list(region[0])[0]
  149. return vector_integrate(field, region)
  150. if isinstance(region[0], GeometryEntity):
  151. regions_list = parametric_region_list(region[0])
  152. result = 0
  153. for reg in regions_list:
  154. result += vector_integrate(field, reg)
  155. return result
  156. return integrate(field, *region)