Source code for gdsfactory.components.optimal_hairpin

from __future__ import annotations

import numpy as np

from gdsfactory.cell import cell
from gdsfactory.component import Component
from gdsfactory.snap import snap_to_grid
from gdsfactory.typings import LayerSpec


[docs] @cell def optimal_hairpin( width: float = 0.2, pitch: float = 0.6, length: float = 10, turn_ratio: float = 4, num_pts: int = 50, layer: LayerSpec = (1, 0), ) -> Component: """Returns an optimally-rounded hairpin geometry, with a 180 degree turn \ on the right end of the polygon connected to two prongs extending towards \ ports on the left end. based on phidl.geometry Args: width: Width of the hairpin leads. pitch: Distance between the two hairpin leads. Must be greater than width. length: Length of the hairpin from the connectors to the opposite end of the curve. turn_ratio: int or float Specifies how much of the hairpin is dedicated to the 180 degree turn. A turn_ratio of 10 will result in 20% of the hairpin being comprised of the turn. num_pts: Number of points constituting the 180 degree turn. layer: Specific layer(s) to put polygon geometry on. Notes: Hairpin pitch must be greater than width. Optimal structure from https://doi.org/10.1103/PhysRevB.84.174510 Clem, J., & Berggren, K. (2011). Geometry-dependent critical currents in superconducting nanocircuits. Physical Review B, 84(17), 1–27. """ # ========================================================================== # Create the basic geometry # ========================================================================== a = (pitch + width) / 2 y = -(pitch - width) / 2 x = -pitch dl = width / (num_pts * 2) n = 0 # Get points of ideal curve from conformal mapping # TODO This is an inefficient way of finding points that you need xpts = [x] ypts = [y] while (y < 0) & (n < 1e6): s = x + 1j * y w = np.sqrt(1 - np.exp(np.pi * s / a)) wx = np.real(w) wy = np.imag(w) wx = wx / np.sqrt(wx**2 + wy**2) wy = wy / np.sqrt(wx**2 + wy**2) x = x + wx * dl y = y + wy * dl xpts.append(x) ypts.append(y) n += 1 ypts[-1] = 0 # Set last point be on the x=0 axis for sake of cleanliness ds_factor = len(xpts) // num_pts xpts = xpts[::-ds_factor] xpts = xpts[::-1] # This looks confusing, but it's just flipping the arrays around ypts = ypts[::-ds_factor] ypts = ypts[::-1] # so the last point is guaranteed to be included when downsampled # Add points for the rest of meander xpts.append(xpts[-1] + turn_ratio * width) ypts.append(0) xpts.append(xpts[-1]) ypts.append(-a) xpts.append(xpts[0]) ypts.append(-a) xpts.append(max(xpts) - length) ypts.append(-a) xpts.append(xpts[-1]) ypts.append(-a + width) xpts.append(xpts[0]) ypts.append(ypts[0]) xpts = snap_to_grid(xpts) ypts = snap_to_grid(ypts) # ========================================================================== # Create a blank device, add the geometry, and define the ports # ========================================================================== c = Component() c.add_polygon([xpts, ypts], layer=layer) c.add_polygon([xpts, -ypts], layer=layer) xports = min(xpts) yports = -a + width / 2 c.add_port( name="e1", center=(xports, -yports), width=width, orientation=180, layer=layer, port_type="electrical", ) c.add_port( name="e2", center=(xports, yports), width=width, orientation=180, layer=layer, port_type="electrical", ) return c
if __name__ == "__main__": c = optimal_hairpin() c.show(show_ports=True)