Source code for ihp.cells.inductors

"""Inductor components for IHP PDK."""

import math

import gdsfactory as gf
from gdsfactory import Component
from gdsfactory.typings import LayerSpec, LayerSpecs


def snap_to_grid(p, grid: float = 0.005):
    return round(p / grid) * grid


[docs] @gf.cell def inductor2( width: float = 2.0, space: float = 2.1, diameter: float = 25.35, vias_width: float = 0.9, resistance: float = 0.5777, inductance: float = 33.303e-12, terminal_1_length: float = 30.0, turns: int = 1, layer_metal_1: LayerSpec = "TopMetal1drawing", layer_metal_2: LayerSpec = "TopMetal2drawing", layer_inductor: LayerSpec = "INDdrawing", layer_metal_1_pin: LayerSpec = "TopMetal2pin", layer_metal_2_pin: LayerSpec = "TopMetal2pin", layer_ind_pin: LayerSpec = "INDpin", layer_via: LayerSpec = "TopVia2drawing", layers_no_fill: LayerSpecs = ( "Activnofill", "GatPolynofill", "Metal1nofill", "Metal2nofill", "Metal3nofill", "Metal4nofill", "Metal5nofill", "TopMetal1nofill", "TopMetal2nofill", "PWellblock", "NoRCXdrawing", ), ) -> Component: """Create a 2-turn inductor. Args: width: Width of the inductor trace in micrometers. space: Space between turns in micrometers. diameter: Inner diameter in micrometers. vias_width: Width of vias in micrometers (only when turns > 2) resistance: Resistance in ohms. inductance: Inductance in henries. terminal_1_length: Length of the shorter terminal turns: Number of turns (default 1 for inductor2). Returns: Component with inductor layout. """ if not isinstance(turns, int) or turns < 1: raise ValueError("turns must be an integer >= 1") c = Component() Pin_layers_1 = [layer_metal_1_pin, layer_ind_pin] Pin_layers_2 = [layer_metal_2_pin, layer_ind_pin] w = snap_to_grid(width, grid=0.005 * 2) s = snap_to_grid(space) d = snap_to_grid(diameter, grid=0.005 * 2) apothem_innermost = d / 2 vertex_angle = math.pi / 4.0 # 45° half_vertex_angle = vertex_angle / 2 # 22.5° length_short_terminal = terminal_1_length length_long_terminal = length_short_terminal + w + (w + s) * (turns - 1) octagon_center_offset_y = length_long_terminal + apothem_innermost # Add inductor layer outer_polygon_pts = [] for i in range(8): r_outer = octagon_center_offset_y / math.cos(half_vertex_angle) angle = i * vertex_angle + half_vertex_angle x = snap_to_grid(r_outer * math.cos(angle)) y = snap_to_grid(r_outer * math.sin(angle) + octagon_center_offset_y) outer_polygon_pts.append((x, y)) c.add_polygon(points=outer_polygon_pts, layer=layer_inductor) # Add No fill layers for layer in layers_no_fill: c.add_polygon(points=outer_polygon_pts, layer=layer) # Handle the terminals and pins of the inductor if turns == 1: port1_single_turn = c << gf.components.rectangle( size=(w, length_short_terminal + w), layer=layer_metal_2 ) port1_single_turn.move((-w - s / 2, 0)) c.add_port( name="P1", center=(-w / 2 - s / 2, 0), width=w, orientation=270, layer=layer_metal_2, ) for layer in Pin_layers_2: pin_1_trace = c << gf.components.rectangle(size=(w, w), layer=layer) pin_1_trace.move((-w - s / 2, 0)) port2_single_turn = c << gf.components.rectangle( size=(w, length_short_terminal + w), layer=layer_metal_2 ) port2_single_turn.move((s / 2, 0)) c.add_port( name="P2", center=(w / 2 + s / 2, 0), width=w, orientation=270, layer=layer_metal_2, ) for layer in Pin_layers_2: pin_2_trace = c << gf.components.rectangle(size=(w, w), layer=layer) pin_2_trace.move((s / 2, 0)) else: port_short = c << gf.components.rectangle( size=(w, length_short_terminal), layer=layer_metal_2 ) port_short.move((-w / 2, 0)) c.add_port( name="P1", center=(0, 0), width=w, orientation=270, layer=layer_metal_2 ) for layer in Pin_layers_2: pin_short_trace = c << gf.components.rectangle(size=(w, w), layer=layer) pin_short_trace.move((-w / 2, 0)) port_long_1 = c << gf.components.rectangle( size=(w, length_long_terminal), layer=layer_metal_1 ) port_long_1.move((-(w + s) - w / 2, 0)) c.add_port( name="P2", center=(-(w + s), 0), width=w, orientation=270, layer=layer_metal_1, ) for layer in Pin_layers_1: pin_long1_trace = c << gf.components.rectangle(size=(w, w), layer=layer) pin_long1_trace.move((-(w + s) - w / 2, 0)) port_long_2 = c << gf.components.rectangle( size=(w, length_long_terminal), layer=layer_metal_1 ) port_long_2.move(((w + s) - w / 2, 0)) c.add_port( name="P3", center=(w + s, 0), width=w, orientation=270, layer=layer_metal_1 ) for layer in Pin_layers_1: pin_long1_trace = c << gf.components.rectangle(size=(w, w), layer=layer) pin_long1_trace.move((w + s - w / 2, 0)) # We break down the body of inductor into 3 sections for k in range(turns): apothem = (apothem_innermost + w / 2) + (w + s) * k half_octagon_side = apothem * math.tan(half_vertex_angle) # Step 1a: We handle the left semi-octagon loops x = (-s / 2) if turns == 1 else (-s - w / 2) left_octagon_fragment = [ (x, octagon_center_offset_y + apothem), (-half_octagon_side, octagon_center_offset_y + apothem), (-apothem, octagon_center_offset_y + half_octagon_side), (-apothem, octagon_center_offset_y - half_octagon_side), (-half_octagon_side, octagon_center_offset_y - apothem), (x, octagon_center_offset_y - apothem), ] left_path = gf.Path(left_octagon_fragment) _ = c << gf.path.extrude(left_path, layer=layer_metal_2, width=w) # Step 1b: We handle the right semi-octagon loops x = (s / 2) if turns == 1 else (s + w / 2) right_octagon_fragment = [ (x, octagon_center_offset_y + apothem), (half_octagon_side, octagon_center_offset_y + apothem), (apothem, octagon_center_offset_y + half_octagon_side), (apothem, octagon_center_offset_y - half_octagon_side), (half_octagon_side, octagon_center_offset_y - apothem), (x, octagon_center_offset_y - apothem), ] right_path = gf.Path(right_octagon_fragment) _ = c << gf.path.extrude(right_path, layer=layer_metal_2, width=w) # Step 2: We handle the connections of the loops within TM2 if turns == 1: center_fragment = [ (-w - s / 2, octagon_center_offset_y + apothem), (w + s / 2, octagon_center_offset_y + apothem), ] center_path = gf.Path(center_fragment) _ = c << gf.path.extrude(center_path, layer=layer_metal_2, width=w) else: if k == 0: continue center_fragment = [ (-s - w / 2, octagon_center_offset_y - apothem), (s + w / 2, octagon_center_offset_y - apothem), ] center_path = gf.Path(center_fragment) c << gf.path.extrude(center_path, layer=layer_metal_2, width=w) connecting_fragment_TM2 = [ (-s - w / 2, octagon_center_offset_y + apothem - w - s), (-w, octagon_center_offset_y + apothem - w - s), (w, octagon_center_offset_y + apothem), (s + w / 2, octagon_center_offset_y + apothem), ] connecting_path_TM2 = gf.Path(connecting_fragment_TM2) c << gf.path.extrude(connecting_path_TM2, layer=layer_metal_2, width=w) # Step 3: We handle the cross connection of the loops using TM1 and vias if turns > 1: connecting_fragment_TM1 = [ (-s - w / 2 - w, octagon_center_offset_y + apothem), (-w, octagon_center_offset_y + apothem), (w, octagon_center_offset_y + apothem - (w + s) * (turns - 1)), (s + w / 2 + w, octagon_center_offset_y + apothem - (w + s) * (turns - 1)), ] connecting_path_TM1 = gf.Path(connecting_fragment_TM1) c << gf.path.extrude(connecting_path_TM1, layer=layer_metal_1, width=w) offset_x = w / 2 - vias_width / 2 offset_y = vias_width / 2 via_1_trace = c << gf.components.rectangle( size=(vias_width, vias_width), layer=layer_via ) via_1_trace.move( (-s - w / 2 - w + offset_x, octagon_center_offset_y + apothem - offset_y) ) via_2_trace = c << gf.components.rectangle( size=(vias_width, vias_width), layer=layer_via ) via_2_trace.move( ( s + w / 2 + offset_x, octagon_center_offset_y + apothem - (w + s) * (turns - 1) - offset_y, ) ) via_3_trace = c << gf.components.rectangle( size=(vias_width, vias_width), layer=layer_via ) via_3_trace.move( ( -s - w / 2 - w + offset_x, octagon_center_offset_y - apothem + (w + s) * (turns - 1) - offset_y, ) ) via_4_trace = c << gf.components.rectangle( size=(vias_width, vias_width), layer=layer_via ) via_4_trace.move( ( s + w / 2 + offset_x, octagon_center_offset_y - apothem + (w + s) * (turns - 1) - offset_y, ) ) # Add metadata c.info["resistance"] = resistance c.info["inductance"] = inductance c.info["model"] = "inductor2" c.info["turns"] = turns c.info["width"] = width c.info["space"] = space c.info["diameter"] = diameter return c
[docs] @gf.cell def inductor3( width: float = 2.0, space: float = 2.1, diameter: float = 25.84, vias_width: float = 0.9, resistance: float = 1.386, inductance: float = 221.5e-12, terminal_1_length: float = 30.0, turns: int = 2, layer_metal_1: LayerSpec = "TopMetal1drawing", layer_metal_2: LayerSpec = "TopMetal2drawing", layer_inductor: LayerSpec = "INDdrawing", layer_metal_1_pin: LayerSpec = "TopMetal2pin", layer_metal_2_pin: LayerSpec = "TopMetal2pin", layer_ind_pin: LayerSpec = "INDpin", layer_via: LayerSpec = "TopVia2drawing", layers_no_fill: LayerSpecs = ( "Activnofill", "GatPolynofill", "Metal1nofill", "Metal2nofill", "Metal3nofill", "Metal4nofill", "Metal5nofill", "TopMetal1nofill", "TopMetal2nofill", "PWellblock", "NoRCXdrawing", ), ) -> Component: """Create a 3-turn inductor. Args: width: Width of the inductor trace in micrometers. space: Space between turns in micrometers. diameter: Inner diameter in micrometers. vias_width: Width of vias in micrometers (only when turns > 2) resistance: Resistance in ohms. inductance: Inductance in henries. terminal_1_length: Length of the shorter terminal turns: Number of turns (default 2 for inductor3). Returns: Component with inductor layout. """ # Use inductor2 as base with different default parameters return inductor2( width=width, space=space, diameter=diameter, vias_width=vias_width, resistance=resistance, inductance=inductance, terminal_1_length=terminal_1_length, turns=turns, layer_metal_1=layer_metal_1, layer_metal_2=layer_metal_2, layer_inductor=layer_inductor, layer_metal_1_pin=layer_metal_1_pin, layer_metal_2_pin=layer_metal_2_pin, layer_ind_pin=layer_ind_pin, layer_via=layer_via, layers_no_fill=layers_no_fill, )
if __name__ == "__main__": from gdsfactory.difftest import xor from ihp import PDK from ihp.cells import fixed PDK.activate() # Test the components c0 = fixed.inductor3() # original c1 = inductor3() # New c = xor(c0, c1) c.show() # c0 = fixed.inductor2() # original # c1 = inductor2() # New # c = xor(c0, c1) # c.show() # c = inductor2(turns=100) # c.show()