123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- from sympy.core.singleton import S
- from sympy.simplify.simplify import simplify
- from sympy.core import Basic, diff
- from sympy.core.sorting import default_sort_key
- from sympy.matrices import Matrix
- from sympy.vector import (CoordSys3D, Vector, ParametricRegion,
- parametric_region_list, ImplicitRegion)
- from sympy.vector.operators import _get_coord_systems
- from sympy.integrals import Integral, integrate
- from sympy.utilities.iterables import topological_sort
- from sympy.geometry.entity import GeometryEntity
- class ParametricIntegral(Basic):
- """
- Represents integral of a scalar or vector field
- over a Parametric Region
- Examples
- ========
- >>> from sympy import cos, sin, pi
- >>> from sympy.vector import CoordSys3D, ParametricRegion, ParametricIntegral
- >>> from sympy.abc import r, t, theta, phi
- >>> C = CoordSys3D('C')
- >>> curve = ParametricRegion((3*t - 2, t + 1), (t, 1, 2))
- >>> ParametricIntegral(C.x, curve)
- 5*sqrt(10)/2
- >>> length = ParametricIntegral(1, curve)
- >>> length
- sqrt(10)
- >>> semisphere = ParametricRegion((2*sin(phi)*cos(theta), 2*sin(phi)*sin(theta), 2*cos(phi)),\
- (theta, 0, 2*pi), (phi, 0, pi/2))
- >>> ParametricIntegral(C.z, semisphere)
- 8*pi
- >>> ParametricIntegral(C.j + C.k, ParametricRegion((r*cos(theta), r*sin(theta)), r, theta))
- 0
- """
- def __new__(cls, field, parametricregion):
- coord_set = _get_coord_systems(field)
- if len(coord_set) == 0:
- coord_sys = CoordSys3D('C')
- elif len(coord_set) > 1:
- raise ValueError
- else:
- coord_sys = next(iter(coord_set))
- if parametricregion.dimensions == 0:
- return S.Zero
- base_vectors = coord_sys.base_vectors()
- base_scalars = coord_sys.base_scalars()
- parametricfield = field
- r = Vector.zero
- for i in range(len(parametricregion.definition)):
- r += base_vectors[i]*parametricregion.definition[i]
- if len(coord_set) != 0:
- for i in range(len(parametricregion.definition)):
- parametricfield = parametricfield.subs(base_scalars[i], parametricregion.definition[i])
- if parametricregion.dimensions == 1:
- parameter = parametricregion.parameters[0]
- r_diff = diff(r, parameter)
- lower, upper = parametricregion.limits[parameter][0], parametricregion.limits[parameter][1]
- if isinstance(parametricfield, Vector):
- integrand = simplify(r_diff.dot(parametricfield))
- else:
- integrand = simplify(r_diff.magnitude()*parametricfield)
- result = integrate(integrand, (parameter, lower, upper))
- elif parametricregion.dimensions == 2:
- u, v = cls._bounds_case(parametricregion.parameters, parametricregion.limits)
- r_u = diff(r, u)
- r_v = diff(r, v)
- normal_vector = simplify(r_u.cross(r_v))
- if isinstance(parametricfield, Vector):
- integrand = parametricfield.dot(normal_vector)
- else:
- integrand = parametricfield*normal_vector.magnitude()
- integrand = simplify(integrand)
- lower_u, upper_u = parametricregion.limits[u][0], parametricregion.limits[u][1]
- lower_v, upper_v = parametricregion.limits[v][0], parametricregion.limits[v][1]
- result = integrate(integrand, (u, lower_u, upper_u), (v, lower_v, upper_v))
- else:
- variables = cls._bounds_case(parametricregion.parameters, parametricregion.limits)
- coeff = Matrix(parametricregion.definition).jacobian(variables).det()
- integrand = simplify(parametricfield*coeff)
- l = [(var, parametricregion.limits[var][0], parametricregion.limits[var][1]) for var in variables]
- result = integrate(integrand, *l)
- if not isinstance(result, Integral):
- return result
- else:
- return super().__new__(cls, field, parametricregion)
- @classmethod
- def _bounds_case(cls, parameters, limits):
- V = list(limits.keys())
- E = list()
- for p in V:
- lower_p = limits[p][0]
- upper_p = limits[p][1]
- lower_p = lower_p.atoms()
- upper_p = upper_p.atoms()
- for q in V:
- if p == q:
- continue
- if lower_p.issuperset({q}) or upper_p.issuperset({q}):
- E.append((p, q))
- if not E:
- return parameters
- else:
- return topological_sort((V, E), key=default_sort_key)
- @property
- def field(self):
- return self.args[0]
- @property
- def parametricregion(self):
- return self.args[1]
- def vector_integrate(field, *region):
- """
- Compute the integral of a vector/scalar field
- over a a region or a set of parameters.
- Examples
- ========
- >>> from sympy.vector import CoordSys3D, ParametricRegion, vector_integrate
- >>> from sympy.abc import x, y, t
- >>> C = CoordSys3D('C')
- >>> region = ParametricRegion((t, t**2), (t, 1, 5))
- >>> vector_integrate(C.x*C.i, region)
- 12
- Integrals over some objects of geometry module can also be calculated.
- >>> from sympy.geometry import Point, Circle, Triangle
- >>> c = Circle(Point(0, 2), 5)
- >>> vector_integrate(C.x**2 + C.y**2, c)
- 290*pi
- >>> triangle = Triangle(Point(-2, 3), Point(2, 3), Point(0, 5))
- >>> vector_integrate(3*C.x**2*C.y*C.i + C.j, triangle)
- -8
- Integrals over some simple implicit regions can be computed. But in most cases,
- it takes too long to compute over them. This is due to the expressions of parametric
- representation becoming large.
- >>> from sympy.vector import ImplicitRegion
- >>> c2 = ImplicitRegion((x, y), (x - 2)**2 + (y - 1)**2 - 9)
- >>> vector_integrate(1, c2)
- 6*pi
- Integral of fields with respect to base scalars:
- >>> vector_integrate(12*C.y**3, (C.y, 1, 3))
- 240
- >>> vector_integrate(C.x**2*C.z, C.x)
- C.x**3*C.z/3
- >>> vector_integrate(C.x*C.i - C.y*C.k, C.x)
- (Integral(C.x, C.x))*C.i + (Integral(-C.y, C.x))*C.k
- >>> _.doit()
- C.x**2/2*C.i + (-C.x*C.y)*C.k
- """
- if len(region) == 1:
- if isinstance(region[0], ParametricRegion):
- return ParametricIntegral(field, region[0])
- if isinstance(region[0], ImplicitRegion):
- region = parametric_region_list(region[0])[0]
- return vector_integrate(field, region)
- if isinstance(region[0], GeometryEntity):
- regions_list = parametric_region_list(region[0])
- result = 0
- for reg in regions_list:
- result += vector_integrate(field, reg)
- return result
- return integrate(field, *region)
|