import numpy as np
from scipy import pi, exp
from ..Contour import Contour
from ..Paths import ComplexLine, ComplexArc
[docs]class AnnulusSector(Contour):
"""
A sector of an annulus in the complex plane.
Parameters
----------
center : complex
The center of the annulus sector.
radii : tuple
Tuple of length two of the form (inner_radius, outer_radius)
phiRange : tuple
Tuple of length two of the form (phi0, phi1).
The segment of the contour containing inner and outer circular
arcs will be joined, counter clockwise from phi0 to phi1.
Examples
--------
.. plot::
:include-source:
from numpy import pi
from cxroots import AnnulusSector
annulusSector = AnnulusSector(center=0.2, radii=(0.5, 1.25), phiRange=(-pi/4, pi/4))
annulusSector.show()
.. plot::
:include-source:
from numpy import pi
from cxroots import AnnulusSector
annulusSector = AnnulusSector(center=0.2, radii=(0.5, 1.25), phiRange=(pi/4, -pi/4))
annulusSector.show()
"""
def __init__(self, center, radii, phiRange):
self.center = center
self.axisName = ('r', 'phi')
if phiRange[0] > phiRange[1]:
phiRange = (phiRange[0], phiRange[1]+2*pi)
phi0, phi1 = self.phiRange = phiRange
# r > 0
r0, r1 = self.radii = radii
if r0 < 0 or r1 <= 0:
raise ValueError('Radius > 0')
# verticies [[radius0,phi0],[radius0,phi1],[radius1,phi1],[radius0,phi1]]
self.z1 = z1 = center + r0*exp(1j*phi0)
self.z2 = z2 = center + r1*exp(1j*phi0)
self.z3 = z3 = center + r1*exp(1j*phi1)
self.z4 = z4 = center + r0*exp(1j*phi1)
segments = [ComplexLine(z1,z2),
ComplexArc(center,r1,phi0,phi1-phi0),
ComplexLine(z3,z4),
ComplexArc(center,r0,phi1,phi0-phi1)]
super(AnnulusSector, self).__init__(segments)
def __str__(self):
return 'Annulus sector: center={center.real:.3f}{center.imag:+.3f}i, r0={radii[0]:.3f}, r1={radii[1]:.3f}, phi0={phiRange[0]:.3f}, phi1={phiRange[1]:.3f}'.format(center=self.center, radii=self.radii, phiRange=self.phiRange)
@property
def centralPoint(self):
# get the central point within the contour
r = (self.radii[0] + self.radii[1])/2
phi = (self.phiRange[0] + self.phiRange[1])/2
return r*exp(1j*phi)
@property
def area(self):
return (self.radii[1]**2 - self.radii[0]**2)*abs(self.phiRange[1] - self.phiRange[0])%(2*pi)/2
def contains(self, z):
""" Returns True if the point z lies within the contour, False if otherwise """
angle = np.angle(z - self.center)%(2*pi) # np.angle maps to [-pi,pi]
radiusCorrect = self.radii[0] < abs(z - self.center) < self.radii[1]
phi = np.mod(self.phiRange, 2*pi)
if phi[0] > phi[1]:
angleCorrect = phi[0] < angle <= 2*pi or 0 <= angle < phi[1]
else:
angleCorrect = phi[0] < angle < phi[1]
return radiusCorrect and angleCorrect
def subdivide(self, axis, divisionFactor=0.5):
"""
Subdivide the contour
Parameters
----------
axis : str, can be either 'r' or 'phi'
The axis along which the line subdividing the contour is a constant.
divisionFactor : float in range (0,1), optional
Determines the point along 'axis' at which the line dividing the box is placed
Returns
-------
box1 : AnnulusSector
If axis is 'r' then phiRange and the inner radius is the same as original AnnulusSector
with the outer radius determined by the divisionFactor.
If axis is 'phi' then the radii and phiRange[0] is the same as the original AnnulusSector
with phiRange[1] determined by the divisionFactor.
box2 : AnnulusSector
If axis is 'r' then phiRange and the outer radius is the same as original AnnulusSector
with the inner radius determined equal to the outer radius of box1.
If axis is 'phi' then the radii and phiRange[1] is the same as the original AnnulusSector
with phiRange[0] equal to phiRange[1] of box1.
"""
r0, r1 = self.radii
phi0, phi1 = self.phiRange
if axis == 0 or axis == self.axisName[0]:
divisionPoint = r0 + divisionFactor*(r1-r0)
box1 = AnnulusSector(self.center, [r0, divisionPoint], self.phiRange)
box2 = AnnulusSector(self.center, [divisionPoint, r1], self.phiRange)
# reuse line segments from original box where possible
# this allows the cached integrals to be used
box1.segments[3] = self.segments[3]
box2.segments[1] = self.segments[1]
box1.segments[1]._reversePath = box2.segments[3]
box2.segments[3]._reversePath = box1.segments[1]
elif axis == 1 or axis == self.axisName[1]:
divisionPoint = phi0 + divisionFactor*(phi1-phi0)
box1 = AnnulusSector(self.center, self.radii, [phi0, divisionPoint])
box2 = AnnulusSector(self.center, self.radii, [divisionPoint, phi1])
box1.segments[0] = self.segments[0]
box2.segments[2] = self.segments[2]
box1.segments[2]._reversePath = box2.segments[0]
box2.segments[0]._reversePath = box1.segments[2]
for box in [box1, box2]:
box._createdBySubdivisionAxis = axis
box._parentBox = self
self._childBoxes = [box1, box2]
return box1, box2
def randomPoint(self):
"""Returns a random point inside the contour of the AnnulusSector."""
r = np.random.uniform(*self.radii)
phiRange = np.mod(self.phiRange, 2*pi)
if phiRange[0] > phiRange[1]:
phi = random.choice([np.random.uniform(phiRange[0], 2*pi),
np.random.uniform(0, phiRange[1])])
else:
phi = np.random.uniform(*phiRange)
return r*exp(1j*phi) + self.center