Skip to content

Commit

Permalink
Fix flake8 formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
fbeutel committed May 25, 2022
1 parent cc261ce commit 9da6fec
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 40 deletions.
92 changes: 55 additions & 37 deletions gdshelpers/parts/spiral.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,34 +120,39 @@ def _example_old():

def _arc_length_indefinite_integral(theta, a, b):
"""
The indefinite integral
The indefinite integral
.. math::
\f{x} = \int r^2 + \frac{\,dr}{\,d\theta}\,d\theta
which is needed to calculate the arc length of a spiral.
"""
return np.sqrt( np.square((a + b * theta)) + np.square(b) ) * (a+b*theta) / (2*b) + \
0.5 * b * np.log(np.sqrt( np.square(a + b*theta) + np.square(b) ) + a + b*theta )
return np.sqrt(np.square((a + b * theta)) + np.square(b)) * (a+b*theta) / (2*b) + \
0.5 * b * np.log(np.sqrt(np.square(a + b*theta) + np.square(b)) + a + b * theta)


def _arc_length_integral(theta, a, b, offset):
return _arc_length_indefinite_integral(theta, a, b) - _arc_length_indefinite_integral(0, a, b)


def _spiral_length_angle(theta, a, b, offset, out_angle):
_, _, circle_length = _circle_segment_params(a, b)
return (_arc_length_integral(theta, a - offset, b, offset) + # Inward spiral
2*circle_length + #2 *np.pi * a + # Two semi-circles in the center
_arc_length_integral(theta + out_angle, a + offset, b, offset)) # Outward spiral
return (_arc_length_integral(theta, a - offset, b, offset) + # Inward spiral
2 * circle_length + # 2 *np.pi * a + # Two semi-circles in the center
_arc_length_integral(theta + out_angle, a + offset, b, offset)) # Outward spiral


def _spiral_length_inline(theta, a, b, offset):
return (_spiral_length_angle(theta, a, b, 0, offset) +
(a + b*(theta+0.5*np.pi)) - (0.5 * a) +
np.pi * (0.5 * a) + # two bends
(2*(a + b * theta) - 2*(0.5 * a))) # from endpoint to startpoint height
(a + b*(theta+0.5*np.pi)) - (0.5 * a) +
np.pi * (0.5 * a) + # two bends
(2*(a + b * theta) - 2*(0.5 * a))) # from endpoint to startpoint height


def _spiral_length_inline_rel(theta, a, b, offset):
return (_spiral_length_inline(theta, a, b, offset) -
(a + b * (theta + 0.5 * np.pi) + (0.5 * a))) # subtract the direct path from input to output
(a + b * (theta + 0.5 * np.pi) + (0.5 * a))) # subtract the direct path from input to output


def _spiral_theta(length, wg_width, gap, min_bend_radius, offset, length_function, *args):
"""
Expand All @@ -161,29 +166,32 @@ def _spiral_theta(length, wg_width, gap, min_bend_radius, offset, length_functio
b = 2*(np.sum(wg_width) + gap) / (2.*np.pi)
return fsolve(lambda x: length_function(x, a, b, offset, *args) - length, 20*np.pi)


def _circle_segment_params(a, b):
"""
Calculate the inner semi-circle segments.
Since the facet of the spiral in the center has a slightly different angle than the tangent of a circle with the min_bend_radius
we don't use a full semi circle, but only start the part of the semi circle at the point where the tangent matches the spiral tangent.
This semi-circle segment needs to be extended in height by a straight piece (missing_height) to connect the spiral with it's center point.
Since the facet of the spiral in the center has a slightly different angle than the tangent of a circle with the
min_bend_radius we don't use a full semi circle, but only start the part of the semi circle at the point where the
tangent matches the spiral tangent. This semi-circle segment needs to be extended in height by a straight piece
(missing_height) to connect the spiral with it's center point.
Returns circ_angle, missing_height, circ_length
"""
rot_in_1 = np.arctan2(b, a)
circ_angle = 0.5 * np.pi - rot_in_1
circ_seg_height = a * np.sin(circ_angle) # = 2 * min_bend_radius * sin(angle / 2), since full_angle = 2*circ_angle
circ_seg_height = a * np.sin(circ_angle) # = 2 * min_bend_radius * sin(angle / 2), since full_angle = 2*circ_angle
missing_height = a - circ_seg_height
return circ_angle, missing_height, circ_angle * a + missing_height


def _spiral_out_path(t, a, b, max_theta, theta_offset=0, direction=-1):
"""
:param theta_offset: This basically rotates the spiral by the given angle, but doesn't change the length of the spiral.
:param theta_offset: This rotates the spiral by the given angle, but doesn't change the length of the spiral.
"""
theta = t * max_theta
r = a + b * theta
#return np.array([r*np.sin(theta + theta_offset), -r*direction*np.cos(theta + theta_offset) + direction*a])# + np.array([0, direction*a])[:, None]
return np.array([r*np.sin(theta + theta_offset), -r*direction*np.cos(theta + theta_offset)])# + np.array([0, direction*a])[:, None]
return np.array([r*np.sin(theta + theta_offset), -r*direction*np.cos(theta + theta_offset)])


def _d_spiral_out_path(t, a, b, max_theta, theta_offset=0, direction=-1):
"""
Expand All @@ -193,7 +201,7 @@ def _d_spiral_out_path(t, a, b, max_theta, theta_offset=0, direction=-1):
theta = t * max_theta
r = a + b * theta
return r * max_theta * np.array([np.cos(theta + theta_offset), direction*np.sin(theta + theta_offset)]) +\
b * max_theta * np.array([np.sin(theta + theta_offset), -direction*np.cos(theta + theta_offset)])
b * max_theta * np.array([np.sin(theta + theta_offset), -direction*np.cos(theta + theta_offset)])


class ArchSpiral:
Expand All @@ -206,7 +214,8 @@ class ArchSpiral:
Output on the same height and direction as input.
* `"inline_rel"`
Output on the same height and direction as input, but the considered length is only the difference of the spiral compared to a straight path.
Output on the same height and direction as input, but the considered length is only the difference of the
spiral compared to a straight path.
This is useful when building MZIs, where the other path is parallel to the spiral.
* `"opposite"`
Expand All @@ -221,16 +230,19 @@ class ArchSpiral:
* `<phi>`, where phi is the angle where the output should be located
"""

def __init__(self, origin, angle, width, gap, min_bend_radius, theta, output_type='opposite', offset=0, winding_direction='right', sample_distance=0.50, sample_points=100):
def __init__(self, origin, angle, width, gap, min_bend_radius, theta, output_type='opposite', offset=0,
winding_direction='right', sample_distance=0.50, sample_points=100):
"""
Create an archimedean spiral following the spiral equation :math:`r = a + b \theta`.
:param origin:
:param angle:
:param width:
:param gap: Gap between the waveguide. Since this is an archimedean spiral, the gap is constant across the whole spiral.
:param gap: Gap between the waveguide. Since this is an archimedean spiral, the gap is constant across the
whole spiral.
:param min_bend_radius: The minimum bend radius. This will set the bend of the two semi-circles in the center.
It follows that `a = 2 * min_bend_radius`, where `a` as defined in the spiral equation above.
It follows that `a = 2 * min_bend_radius`, where `a` as defined in the spiral equation
above.
:param theta: The total angle to turn
"""
self._origin_port = Port(origin, angle, width)
Expand Down Expand Up @@ -264,7 +276,8 @@ def make_at_port(cls, port, **kwargs):
return cls(port.origin, port.angle, port.width, **default_port_param)

@classmethod
def make_at_port_with_length(cls, port, gap, min_bend_radius, target_length, output_type='opposite', offset=0, **kwargs):
def make_at_port_with_length(cls, port, gap, min_bend_radius, target_length, output_type='opposite', offset=0,
**kwargs):
if output_type == "inline":
length_fn = [_spiral_length_inline]
elif output_type == "inline_rel":
Expand All @@ -277,7 +290,8 @@ def make_at_port_with_length(cls, port, gap, min_bend_radius, target_length, out
length_fn = [_spiral_length_angle, output_type]

theta = float(_spiral_theta(target_length, port.width, gap, min_bend_radius, offset, *length_fn))
return cls.make_at_port(port, gap=gap, min_bend_radius=min_bend_radius, theta=theta, output_type=output_type, offset=offset, **kwargs)
return cls.make_at_port(port, gap=gap, min_bend_radius=min_bend_radius, theta=theta, output_type=output_type,
offset=offset, **kwargs)

@property
def width(self):
Expand All @@ -302,10 +316,10 @@ def _generate(self):
a = 2*self.min_bend_radius
a_in, a_out = a - self.winding_direction * self.offset, a + self.winding_direction * self.offset
b = 2*(np.sum(self.width) + self.gap) / (2.*np.pi)

# Rotate the spiral such that the input facet of the spiral has an [1,0] normal vector.
# (The tangent of a archimedean spiral is slightly different to that of a circle, so at the top it has a normal vector slightly
# different that [1,0].)
# (The tangent of a archimedean spiral is slightly different to that of a circle, so at the top it has a normal
# vector slightly different that [1,0].)
in_args = dict(a=a_in, b=b, max_theta=self.total_theta, theta_offset=0, direction=self.winding_direction)
d_in_args = dict(a=a, b=b, max_theta=self.total_theta, theta_offset=0, direction=self.winding_direction)
d_in_0 = _d_spiral_out_path(1, **d_in_args)
Expand All @@ -325,7 +339,8 @@ def _generate(self):

# Generate the spiral
if self.output_type != "single_outside":
self._wg.add_parameterized_path(lambda x: -_spiral_out_path(1-x, **in_args) - in_offset[:, None], sample_distance=self.sample_distance, sample_points=self.sample_points,
self._wg.add_parameterized_path(lambda x: -_spiral_out_path(1-x, **in_args) - in_offset[:, None],
sample_distance=self.sample_distance, sample_points=self.sample_points,
path_derivative=lambda x: _d_spiral_out_path(1-x, **d_in_args),
path_function_supports_numpy=True)

Expand All @@ -343,7 +358,8 @@ def _generate(self):
self._wg.add_bend(self.winding_direction*circ_angle, r)

if self.output_type != "single_inside":
self._wg.add_parameterized_path(lambda x: _spiral_out_path(x, **out_args) - out_offset[:, None], sample_distance=self.sample_distance, sample_points=self.sample_points,
self._wg.add_parameterized_path(lambda x: _spiral_out_path(x, **out_args) - out_offset[:, None],
sample_distance=self.sample_distance, sample_points=self.sample_points,
path_derivative=lambda x: _d_spiral_out_path(x, **dout_args),
path_function_supports_numpy=True)

Expand All @@ -366,15 +382,19 @@ def get_shapely_object(self):
def demo_spiral(origin, output_type, target_length, gap, port_y_offset=0, width=1):
wg = Waveguide(origin + np.array([0, port_y_offset]), 0, width)
wg.add_straight_segment(30)
spiral = ArchSpiral.make_at_port_with_length(wg.current_port, gap=gap, min_bend_radius=35., target_length=target_length, output_type=output_type, sample_distance=1)
text = Text(np.array([150, -130]) + origin, 20, "output: {}\n\nlength: {} um\nreal_length: {:.4f}um".format(output_type, target_length, spiral.length))
spiral = ArchSpiral.make_at_port_with_length(
wg.current_port, gap=gap, min_bend_radius=35., target_length=target_length, output_type=output_type,
sample_distance=1)
text = Text(np.array([150, -130]) + origin, 20,
"output: {}\n\nlength: {} um\nreal_length: {:.4f}um".format(output_type, target_length,
spiral.length))
spiral.wg.add_straight_segment(30)
cell.add_to_layer(1, wg, spiral)
cell.add_to_layer(2, text)

# Create normal demo spirals
for i,output_type in enumerate(['opposite', 'inline', 'inline_rel', -0.5*np.pi, 0.25*np.pi, np.pi]):
demo_spiral(((i//4)*700, (i%4)*250), output_type, 5000, gap=3., width=1)
for i, output_type in enumerate(['opposite', 'inline', 'inline_rel', -0.5*np.pi, 0.25*np.pi, np.pi]):
demo_spiral(((i // 4)*700, (i % 4)*250), output_type, 5000, gap=3., width=1)

# Create spirals with single turn
demo_spiral((1*700, 2*250), 'single_inside', 2000, gap=1.5)
Expand All @@ -386,7 +406,5 @@ def demo_spiral(origin, output_type, target_length, gap, port_y_offset=0, width=
demo_spiral((2000, 3*800), 'inline', 11000, gap=21., width=[1,5,1,5,1])
demo_spiral((2000, 4*800), 'inline', 11000, gap=54., width=1)"""



cell.show()
cell.save("spiral_test")
6 changes: 3 additions & 3 deletions gdshelpers/tests/test_arch_spiral.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ def test_inline(self):

wg = Waveguide(np.array(start_origin), 0, 1.3)
wg.add_straight_segment(30)
spiral = ArchSpiral.make_at_port_with_length(wg.current_port, gap=80., min_bend_radius=35., target_length=20000, output_type='inline', sample_distance=50)
spiral = ArchSpiral.make_at_port_with_length(wg.current_port, gap=80., min_bend_radius=35., target_length=20000,
output_type='inline', sample_distance=50)
spiral.wg.add_straight_segment(30)

self.assertAlmostEqual(spiral.out_port.origin[1], start_origin[1])

self.assertAlmostEqual(spiral.out_port.origin[1], start_origin[1])

0 comments on commit 9da6fec

Please sign in to comment.