@@ -120,34 +120,39 @@ def _example_old():
120
120
121
121
def _arc_length_indefinite_integral (theta , a , b ):
122
122
"""
123
- The indefinite integral
123
+ The indefinite integral
124
124
125
125
.. math::
126
126
\f {x} = \int r^2 + \f rac{\,dr}{\,d\t heta}\,d\t heta
127
-
127
+
128
128
which is needed to calculate the arc length of a spiral.
129
129
"""
130
- return np .sqrt ( np .square ((a + b * theta )) + np .square (b ) ) * (a + b * theta ) / (2 * b ) + \
131
- 0.5 * b * np .log (np .sqrt ( np .square (a + b * theta ) + np .square (b ) ) + a + b * theta )
130
+ return np .sqrt (np .square ((a + b * theta )) + np .square (b )) * (a + b * theta ) / (2 * b ) + \
131
+ 0.5 * b * np .log (np .sqrt (np .square (a + b * theta ) + np .square (b )) + a + b * theta )
132
+
132
133
133
134
def _arc_length_integral (theta , a , b , offset ):
134
135
return _arc_length_indefinite_integral (theta , a , b ) - _arc_length_indefinite_integral (0 , a , b )
135
136
137
+
136
138
def _spiral_length_angle (theta , a , b , offset , out_angle ):
137
139
_ , _ , circle_length = _circle_segment_params (a , b )
138
- return (_arc_length_integral (theta , a - offset , b , offset ) + # Inward spiral
139
- 2 * circle_length + #2 *np.pi * a + # Two semi-circles in the center
140
- _arc_length_integral (theta + out_angle , a + offset , b , offset )) # Outward spiral
140
+ return (_arc_length_integral (theta , a - offset , b , offset ) + # Inward spiral
141
+ 2 * circle_length + # 2 *np.pi * a + # Two semi-circles in the center
142
+ _arc_length_integral (theta + out_angle , a + offset , b , offset )) # Outward spiral
143
+
141
144
142
145
def _spiral_length_inline (theta , a , b , offset ):
143
146
return (_spiral_length_angle (theta , a , b , 0 , offset ) +
144
- (a + b * (theta + 0.5 * np .pi )) - (0.5 * a ) +
145
- np .pi * (0.5 * a ) + # two bends
146
- (2 * (a + b * theta ) - 2 * (0.5 * a ))) # from endpoint to startpoint height
147
+ (a + b * (theta + 0.5 * np .pi )) - (0.5 * a ) +
148
+ np .pi * (0.5 * a ) + # two bends
149
+ (2 * (a + b * theta ) - 2 * (0.5 * a ))) # from endpoint to startpoint height
150
+
147
151
148
152
def _spiral_length_inline_rel (theta , a , b , offset ):
149
153
return (_spiral_length_inline (theta , a , b , offset ) -
150
- (a + b * (theta + 0.5 * np .pi ) + (0.5 * a ))) # subtract the direct path from input to output
154
+ (a + b * (theta + 0.5 * np .pi ) + (0.5 * a ))) # subtract the direct path from input to output
155
+
151
156
152
157
def _spiral_theta (length , wg_width , gap , min_bend_radius , offset , length_function , * args ):
153
158
"""
@@ -161,29 +166,32 @@ def _spiral_theta(length, wg_width, gap, min_bend_radius, offset, length_functio
161
166
b = 2 * (np .sum (wg_width ) + gap ) / (2. * np .pi )
162
167
return fsolve (lambda x : length_function (x , a , b , offset , * args ) - length , 20 * np .pi )
163
168
169
+
164
170
def _circle_segment_params (a , b ):
165
171
"""
166
172
Calculate the inner semi-circle segments.
167
- 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
168
- 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.
169
- 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.
173
+ Since the facet of the spiral in the center has a slightly different angle than the tangent of a circle with the
174
+ 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
175
+ tangent matches the spiral tangent. This semi-circle segment needs to be extended in height by a straight piece
176
+ (missing_height) to connect the spiral with it's center point.
170
177
171
178
Returns circ_angle, missing_height, circ_length
172
179
"""
173
180
rot_in_1 = np .arctan2 (b , a )
174
181
circ_angle = 0.5 * np .pi - rot_in_1
175
- circ_seg_height = a * np .sin (circ_angle ) # = 2 * min_bend_radius * sin(angle / 2), since full_angle = 2*circ_angle
182
+ circ_seg_height = a * np .sin (circ_angle ) # = 2 * min_bend_radius * sin(angle / 2), since full_angle = 2*circ_angle
176
183
missing_height = a - circ_seg_height
177
184
return circ_angle , missing_height , circ_angle * a + missing_height
178
185
186
+
179
187
def _spiral_out_path (t , a , b , max_theta , theta_offset = 0 , direction = - 1 ):
180
188
"""
181
- :param theta_offset: This basically rotates the spiral by the given angle, but doesn't change the length of the spiral.
189
+ :param theta_offset: This rotates the spiral by the given angle, but doesn't change the length of the spiral.
182
190
"""
183
191
theta = t * max_theta
184
192
r = a + b * theta
185
- # return np.array([r*np.sin(theta + theta_offset), -r*direction*np.cos(theta + theta_offset) + direction*a])# + np.array([0, direction*a])[:, None]
186
- return np . array ([ r * np . sin ( theta + theta_offset ), - r * direction * np . cos ( theta + theta_offset )]) # + np.array([0, direction*a])[:, None]
193
+ return np .array ([r * np .sin (theta + theta_offset ), - r * direction * np .cos (theta + theta_offset )])
194
+
187
195
188
196
def _d_spiral_out_path (t , a , b , max_theta , theta_offset = 0 , direction = - 1 ):
189
197
"""
@@ -193,7 +201,7 @@ def _d_spiral_out_path(t, a, b, max_theta, theta_offset=0, direction=-1):
193
201
theta = t * max_theta
194
202
r = a + b * theta
195
203
return r * max_theta * np .array ([np .cos (theta + theta_offset ), direction * np .sin (theta + theta_offset )]) + \
196
- b * max_theta * np .array ([np .sin (theta + theta_offset ), - direction * np .cos (theta + theta_offset )])
204
+ b * max_theta * np .array ([np .sin (theta + theta_offset ), - direction * np .cos (theta + theta_offset )])
197
205
198
206
199
207
class ArchSpiral :
@@ -206,7 +214,8 @@ class ArchSpiral:
206
214
Output on the same height and direction as input.
207
215
208
216
* `"inline_rel"`
209
- 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.
217
+ Output on the same height and direction as input, but the considered length is only the difference of the
218
+ spiral compared to a straight path.
210
219
This is useful when building MZIs, where the other path is parallel to the spiral.
211
220
212
221
* `"opposite"`
@@ -221,16 +230,19 @@ class ArchSpiral:
221
230
* `<phi>`, where phi is the angle where the output should be located
222
231
"""
223
232
224
- 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 ):
233
+ def __init__ (self , origin , angle , width , gap , min_bend_radius , theta , output_type = 'opposite' , offset = 0 ,
234
+ winding_direction = 'right' , sample_distance = 0.50 , sample_points = 100 ):
225
235
"""
226
236
Create an archimedean spiral following the spiral equation :math:`r = a + b \t heta`.
227
-
237
+
228
238
:param origin:
229
239
:param angle:
230
240
:param width:
231
- :param gap: Gap between the waveguide. Since this is an archimedean spiral, the gap is constant across the whole spiral.
241
+ :param gap: Gap between the waveguide. Since this is an archimedean spiral, the gap is constant across the
242
+ whole spiral.
232
243
:param min_bend_radius: The minimum bend radius. This will set the bend of the two semi-circles in the center.
233
- It follows that `a = 2 * min_bend_radius`, where `a` as defined in the spiral equation above.
244
+ It follows that `a = 2 * min_bend_radius`, where `a` as defined in the spiral equation
245
+ above.
234
246
:param theta: The total angle to turn
235
247
"""
236
248
self ._origin_port = Port (origin , angle , width )
@@ -264,7 +276,8 @@ def make_at_port(cls, port, **kwargs):
264
276
return cls (port .origin , port .angle , port .width , ** default_port_param )
265
277
266
278
@classmethod
267
- def make_at_port_with_length (cls , port , gap , min_bend_radius , target_length , output_type = 'opposite' , offset = 0 , ** kwargs ):
279
+ def make_at_port_with_length (cls , port , gap , min_bend_radius , target_length , output_type = 'opposite' , offset = 0 ,
280
+ ** kwargs ):
268
281
if output_type == "inline" :
269
282
length_fn = [_spiral_length_inline ]
270
283
elif output_type == "inline_rel" :
@@ -277,7 +290,8 @@ def make_at_port_with_length(cls, port, gap, min_bend_radius, target_length, out
277
290
length_fn = [_spiral_length_angle , output_type ]
278
291
279
292
theta = float (_spiral_theta (target_length , port .width , gap , min_bend_radius , offset , * length_fn ))
280
- return cls .make_at_port (port , gap = gap , min_bend_radius = min_bend_radius , theta = theta , output_type = output_type , offset = offset , ** kwargs )
293
+ return cls .make_at_port (port , gap = gap , min_bend_radius = min_bend_radius , theta = theta , output_type = output_type ,
294
+ offset = offset , ** kwargs )
281
295
282
296
@property
283
297
def width (self ):
@@ -302,10 +316,10 @@ def _generate(self):
302
316
a = 2 * self .min_bend_radius
303
317
a_in , a_out = a - self .winding_direction * self .offset , a + self .winding_direction * self .offset
304
318
b = 2 * (np .sum (self .width ) + self .gap ) / (2. * np .pi )
305
-
319
+
306
320
# Rotate the spiral such that the input facet of the spiral has an [1,0] normal vector.
307
- # (The tangent of a archimedean spiral is slightly different to that of a circle, so at the top it has a normal vector slightly
308
- # different that [1,0].)
321
+ # (The tangent of a archimedean spiral is slightly different to that of a circle, so at the top it has a normal
322
+ # vector slightly different that [1,0].)
309
323
in_args = dict (a = a_in , b = b , max_theta = self .total_theta , theta_offset = 0 , direction = self .winding_direction )
310
324
d_in_args = dict (a = a , b = b , max_theta = self .total_theta , theta_offset = 0 , direction = self .winding_direction )
311
325
d_in_0 = _d_spiral_out_path (1 , ** d_in_args )
@@ -325,7 +339,8 @@ def _generate(self):
325
339
326
340
# Generate the spiral
327
341
if self .output_type != "single_outside" :
328
- 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 ,
342
+ self ._wg .add_parameterized_path (lambda x : - _spiral_out_path (1 - x , ** in_args ) - in_offset [:, None ],
343
+ sample_distance = self .sample_distance , sample_points = self .sample_points ,
329
344
path_derivative = lambda x : _d_spiral_out_path (1 - x , ** d_in_args ),
330
345
path_function_supports_numpy = True )
331
346
@@ -343,7 +358,8 @@ def _generate(self):
343
358
self ._wg .add_bend (self .winding_direction * circ_angle , r )
344
359
345
360
if self .output_type != "single_inside" :
346
- 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 ,
361
+ self ._wg .add_parameterized_path (lambda x : _spiral_out_path (x , ** out_args ) - out_offset [:, None ],
362
+ sample_distance = self .sample_distance , sample_points = self .sample_points ,
347
363
path_derivative = lambda x : _d_spiral_out_path (x , ** dout_args ),
348
364
path_function_supports_numpy = True )
349
365
@@ -366,15 +382,19 @@ def get_shapely_object(self):
366
382
def demo_spiral (origin , output_type , target_length , gap , port_y_offset = 0 , width = 1 ):
367
383
wg = Waveguide (origin + np .array ([0 , port_y_offset ]), 0 , width )
368
384
wg .add_straight_segment (30 )
369
- 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 )
370
- text = Text (np .array ([150 , - 130 ]) + origin , 20 , "output: {}\n \n length: {} um\n real_length: {:.4f}um" .format (output_type , target_length , spiral .length ))
385
+ spiral = ArchSpiral .make_at_port_with_length (
386
+ wg .current_port , gap = gap , min_bend_radius = 35. , target_length = target_length , output_type = output_type ,
387
+ sample_distance = 1 )
388
+ text = Text (np .array ([150 , - 130 ]) + origin , 20 ,
389
+ "output: {}\n \n length: {} um\n real_length: {:.4f}um" .format (output_type , target_length ,
390
+ spiral .length ))
371
391
spiral .wg .add_straight_segment (30 )
372
392
cell .add_to_layer (1 , wg , spiral )
373
393
cell .add_to_layer (2 , text )
374
394
375
395
# Create normal demo spirals
376
- for i ,output_type in enumerate (['opposite' , 'inline' , 'inline_rel' , - 0.5 * np .pi , 0.25 * np .pi , np .pi ]):
377
- demo_spiral (((i // 4 )* 700 , (i % 4 )* 250 ), output_type , 5000 , gap = 3. , width = 1 )
396
+ for i , output_type in enumerate (['opposite' , 'inline' , 'inline_rel' , - 0.5 * np .pi , 0.25 * np .pi , np .pi ]):
397
+ demo_spiral (((i // 4 )* 700 , (i % 4 )* 250 ), output_type , 5000 , gap = 3. , width = 1 )
378
398
379
399
# Create spirals with single turn
380
400
demo_spiral ((1 * 700 , 2 * 250 ), 'single_inside' , 2000 , gap = 1.5 )
@@ -386,7 +406,5 @@ def demo_spiral(origin, output_type, target_length, gap, port_y_offset=0, width=
386
406
demo_spiral((2000, 3*800), 'inline', 11000, gap=21., width=[1,5,1,5,1])
387
407
demo_spiral((2000, 4*800), 'inline', 11000, gap=54., width=1)"""
388
408
389
-
390
-
391
409
cell .show ()
392
410
cell .save ("spiral_test" )
0 commit comments