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.

207 lines
6.7 KiB

from sympy.core import Basic, diff
from sympy.core.singleton import S
from sympy.core.sorting import default_sort_key
from sympy.matrices import Matrix
from sympy.integrals import Integral, integrate
from sympy.geometry.entity import GeometryEntity
from sympy.simplify.simplify import simplify
from sympy.utilities.iterables import topological_sort
from sympy.vector import (CoordSys3D, Vector, ParametricRegion,
parametric_region_list, ImplicitRegion)
from sympy.vector.operators import _get_coord_systems
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 = []
for p in V:
lower_p = limits[p][0]
upper_p = limits[p][1]
lower_p = lower_p.atoms()
upper_p = upper_p.atoms()
E.extend((p, q) for q in V if p != q and
(lower_p.issuperset({q}) or upper_p.issuperset({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)