Source code for cxroots.contours.AnnulusSector

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