integrals.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. from sympy.core.singleton import S
  2. from sympy.simplify.simplify import simplify
  3. from sympy.core import Basic, diff
  4. from sympy.core.sorting import default_sort_key
  5. from sympy.matrices import Matrix
  6. from sympy.vector import (CoordSys3D, Vector, ParametricRegion,
  7. parametric_region_list, ImplicitRegion)
  8. from sympy.vector.operators import _get_coord_systems
  9. from sympy.integrals import Integral, integrate
  10. from sympy.utilities.iterables import topological_sort
  11. from sympy.geometry.entity import GeometryEntity
  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 = list()
  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. for q in V:
  96. if p == q:
  97. continue
  98. if lower_p.issuperset({q}) or upper_p.issuperset({q}):
  99. E.append((p, q))
  100. if not E:
  101. return parameters
  102. else:
  103. return topological_sort((V, E), key=default_sort_key)
  104. @property
  105. def field(self):
  106. return self.args[0]
  107. @property
  108. def parametricregion(self):
  109. return self.args[1]
  110. def vector_integrate(field, *region):
  111. """
  112. Compute the integral of a vector/scalar field
  113. over a a region or a set of parameters.
  114. Examples
  115. ========
  116. >>> from sympy.vector import CoordSys3D, ParametricRegion, vector_integrate
  117. >>> from sympy.abc import x, y, t
  118. >>> C = CoordSys3D('C')
  119. >>> region = ParametricRegion((t, t**2), (t, 1, 5))
  120. >>> vector_integrate(C.x*C.i, region)
  121. 12
  122. Integrals over some objects of geometry module can also be calculated.
  123. >>> from sympy.geometry import Point, Circle, Triangle
  124. >>> c = Circle(Point(0, 2), 5)
  125. >>> vector_integrate(C.x**2 + C.y**2, c)
  126. 290*pi
  127. >>> triangle = Triangle(Point(-2, 3), Point(2, 3), Point(0, 5))
  128. >>> vector_integrate(3*C.x**2*C.y*C.i + C.j, triangle)
  129. -8
  130. Integrals over some simple implicit regions can be computed. But in most cases,
  131. it takes too long to compute over them. This is due to the expressions of parametric
  132. representation becoming large.
  133. >>> from sympy.vector import ImplicitRegion
  134. >>> c2 = ImplicitRegion((x, y), (x - 2)**2 + (y - 1)**2 - 9)
  135. >>> vector_integrate(1, c2)
  136. 6*pi
  137. Integral of fields with respect to base scalars:
  138. >>> vector_integrate(12*C.y**3, (C.y, 1, 3))
  139. 240
  140. >>> vector_integrate(C.x**2*C.z, C.x)
  141. C.x**3*C.z/3
  142. >>> vector_integrate(C.x*C.i - C.y*C.k, C.x)
  143. (Integral(C.x, C.x))*C.i + (Integral(-C.y, C.x))*C.k
  144. >>> _.doit()
  145. C.x**2/2*C.i + (-C.x*C.y)*C.k
  146. """
  147. if len(region) == 1:
  148. if isinstance(region[0], ParametricRegion):
  149. return ParametricIntegral(field, region[0])
  150. if isinstance(region[0], ImplicitRegion):
  151. region = parametric_region_list(region[0])[0]
  152. return vector_integrate(field, region)
  153. if isinstance(region[0], GeometryEntity):
  154. regions_list = parametric_region_list(region[0])
  155. result = 0
  156. for reg in regions_list:
  157. result += vector_integrate(field, reg)
  158. return result
  159. return integrate(field, *region)