def test_quirky_cases(): pc = ParamContainer() pc.add_param(PCellParameter(name="orange", default=1)) # Don't try to set any value here with pytest.raises( TypeError, match="'ParamContainer' object does not support item assignment" ): pc["orange"] = 2
class DCPad(OrientedCell): """A standard DC pad. Ports: el0 """ params = ParamContainer(pad_width, pad_height, port_width, layer_metal, layer_opening) def draw(self, cell): layout = cell.layout() origin, ex, ey = self.origin_ex_ey() cp = self.params def make_shape_from_dpolygon(dpoly, resize_dx, dbu, layer): dpoly.resize(resize_dx, dbu) # if resize_dx > dbu: # dpoly.round_corners(resize_dx, 100) insert_shape(cell, layer, dpoly) return dpoly def make_pad(origin, pad_width, pad_height, ex, ey): pad_square = rectangle(origin, pad_width, pad_height, ex, ey) make_shape_from_dpolygon(pad_square, 0, layout.dbu, cp.layer_metal) make_shape_from_dpolygon(pad_square, -2.5, layout.dbu, cp.layer_opening) make_pad(origin + cp.pad_height * ey / 2, cp.pad_width, cp.pad_height, ex, ey) port = Port("el0", origin + cp.port_width * ey / 2, -ey, cp.port_width, "el_dc") return cell, {"el0": port}
class PositionMixin(PCell): """ handles the angle_ex parameter """ params = ParamContainer( PCellParameter( name="angle_ex", type=TypeDouble, description="Placement Angle (0, 90, ..)", default=0, )) def origin_ex_ey(self, multiple_of_90=False): # pylint: disable=unused-argument EX = kdb.DVector(1, 0) cp = self.get_cell_params() origin = kdb.DPoint(0, 0) # if 'angle_ex' not in cp.__dict__: # cp.angle_ex = 0 if multiple_of_90: if cp.angle_ex % 90 != 0: raise RuntimeError("Specify an angle multiple of 90 degrees") from math import pi ex = rotate(EX, cp.angle_ex * pi / 180) ey = rotate90(ex) return origin, ex, ey
class OrientedCell(PCell): """A standard cell that has the following parameters: - origin: Point - ex: unit vector of x axis - ey: unit vector of y axis """ params = ParamContainer(origin, ex, ey) def origin_ex_ey(self): origin = DPoint(self.params["origin"]) ex = DVector(self.params.ex) ey = DVector(self.params.ey) return origin, ex, ey
class DCPadArray(DCPadArray): params = ParamContainer( PCellParameter( name="layer_metal", type=TypeLayer, description="Metal Layer", default=EBeam.layers["M1"], ), PCellParameter( name="layer_opening", type=TypeLayer, description="Open Layer", default=EBeam.layers["13_MLopen"], ), )
class DCPadArray(DCPad): params = ParamContainer(pad_array_count, pad_array_pitch) def draw(self, cell): cp = self.params origin, ex, _ = self.origin_ex_ey() ports = dict() for i in range(cp.pad_array_count): dcpad = DCPad(name=f"pad_{i}", params=cp) dc_ports = dcpad.place_cell(cell, origin + cp.pad_array_pitch * i * ex) ports[f"el_{i}"] = dc_ports["el0"].rename(f"el_{i}") # self.add_port(dc_ports["el0"].rename(f"el_{i}")) return cell, ports
class Waveguide_heater(PDKCell): params = ParamContainer( define_param("mh_length", TypeInt, "Heated waveguide length", default=100, unit="um"), define_param("mh_width", TypeDouble, "Heater width", default=5, unit="um"), define_param("w_contacts", TypeDouble, "Contact width", default=10, unit="um"), ) def draw(self, cell): layout = cell.layout() cp = self.get_cell_params() lay = self.get_layers(layout) origin, ex, ey = self.origin_ex_ey() w_mh = cp.mh_width length = cp.mh_length w_contacts = cp.w_contacts input_port_position = origin - length / 2 * ex output_port_postion = origin + length / 2 * ex x_off = length / 2 - w_contacts / 2 y_off = w_mh / 2 + w_contacts / 2 input_contact_position = origin - x_off * ex + y_off * ey output_contact_position = origin + x_off * ex + y_off * ey # draw metal heater layout_waveguide(cell, lay.M_Heater, [input_port_position, output_port_postion], w_mh) # draw metal contacts # Left contacts contact_points = [ origin - x_off * ex + (y_off - w_contacts / 2) * ey, origin - x_off * ex + (y_off + w_contacts / 2) * ey, ] layout_waveguide(cell, lay.M_Heater, contact_points, w_contacts) layout_waveguide(cell, lay.ML, contact_points, w_contacts) # Right contacts contact_points = [ origin + x_off * ex + (y_off - w_contacts / 2) * ey, origin + x_off * ex + (y_off + w_contacts / 2) * ey, ] layout_waveguide(cell, lay.M_Heater, contact_points, w_contacts) layout_waveguide(cell, lay.ML, contact_points, w_contacts) ports = [ Port("el_contact_1", input_contact_position, ey, w_contacts), Port("el_contact_2", output_contact_position, ey, w_contacts), ] return cell, {port.name: port for port in ports}
class MZI_Broadband_DC(Broadband_DC_te1550, YBranch_te1550, Waveguide_heater): params = ParamContainer( define_param("MZI_height", TypeDouble, "interferometer_height of MZI", default=20), define_param("waveguide_length_MZI", TypeDouble, "waveguide_length_MZI", default=100), define_param("layout_ports", TypeBoolean, "Layout Pins?", default=True), define_param("wg_width", TypeInt, "Waveguide width ", default=0.5), ) def draw(self, cell): layout = cell.layout() cp = self.get_cell_params() lay = self.get_layers(layout) origin, ex, ey = self.origin_ex_ey() YBranch_te1550_Cell_0, YBranch_te1550_Ports_0 = YBranch_te1550( "Broadband_DC", params={ "angle_ex": cp.angle_ex }).new_cell(layout) YBranch_te1550_Ports_0 = place_cell(cell, YBranch_te1550_Cell_0, YBranch_te1550_Ports_0, origin) Broadband_DC_Cell, Broadband_DC_Ports = Broadband_DC_te1550( "Broadband_DC", cp).new_cell(layout) Broadband_DC_Ports = place_cell( cell, Broadband_DC_Cell, Broadband_DC_Ports, origin + (cp.waveguide_length_MZI + 100) * ex, ) top_arm_left = (YBranch_te1550_Ports_0["opt2"].position + 25 * ex + cp.MZI_height / 2 * ey) top_arm_right = top_arm_left + cp.waveguide_length_MZI * ex layout_waveguide(cell, lay.Si, [top_arm_left, top_arm_right], 0.5) Heater_1_MZI_Cell, Heater_1_MZI_Ports = Waveguide_heater( "waveguide heater", params={ "wg_length": cp.waveguide_length_MZI, "angle_ex": cp.angle_ex }, ).new_cell(layout) Heater_1_MZI_Ports = place_cell( cell, Heater_1_MZI_Cell, Heater_1_MZI_Ports, YBranch_te1550_Ports_0["opt2"].position + 25 * ex + cp.MZI_height / 2 * ey + cp.waveguide_length_MZI / 2 * ex, ) bottom_arm_left = (YBranch_te1550_Ports_0["opt3"].position + 25 * ex - cp.MZI_height / 2 * ey) bottom_arm_right = bottom_arm_left + cp.waveguide_length_MZI * ex layout_waveguide(cell, lay.Si, [bottom_arm_left, bottom_arm_right], 0.5) from zeropdk.layout.geometry import bezier_optimal def layout_mzi_curve(P0, P1): curve = bezier_optimal(P0, P1, cp.angle_ex, cp.angle_ex) return layout_waveguide(cell, lay.Si, curve, cp.wg_width, smooth=True) layout_mzi_curve(YBranch_te1550_Ports_0["opt2"].position, top_arm_left) layout_mzi_curve(YBranch_te1550_Ports_0["opt3"].position, bottom_arm_left) layout_mzi_curve(top_arm_right, Broadband_DC_Ports["opt1"].position) layout_mzi_curve(bottom_arm_right, Broadband_DC_Ports["opt2"].position) ports = [] ports.append(YBranch_te1550_Ports_0["opt1"].rename("opt_in")) ports.append(Broadband_DC_Ports["opt3"].rename("opt_out_1")) ports.append(Broadband_DC_Ports["opt4"].rename("opt_out_2")) ports.extend(Heater_1_MZI_Ports.values()) if cp.layout_ports: for port in ports: port.draw(cell, lay.PinRec) insert_shape(cell, lay.DevRec, cell.bbox()) return cell, {port.name: port for port in ports}
class PadArray(Pad): params = ParamContainer(pad_array_count)
class Pad(PCell): params = ParamContainer(pad_size)
class EBeamLayersMixin(PCell): """ Abstract class with more concise layer handling """ params = ParamContainer( define_param("silayer", TypeLayer, "Si (1/0)", default=TECHLAYERS["Si"], layertype='Si', zmin=0, zmax=0.22), define_param("sislab", TypeLayer, "Si slab (2/0)", default=TECHLAYERS["Si slab"], layertype='Si', zmin=0, zmax=0.09), define_param( "31_Si_p6nm", TypeLayer, "'31_Si_p6nm' (31/0)", default=TECHLAYERS["31_Si_p6nm"], ), define_param("textl", TypeLayer, "Text (10/0)", default=TECHLAYERS["Text"]), define_param("si_n", TypeLayer, "'Si N' (20/0)", default=TECHLAYERS["Si N"], layertype='doping', dopinglevel=-5E17), define_param("si_npp", TypeLayer, "'Si N++' (24/0)", default=TECHLAYERS["Si N++"], layertype='doping', dopinglevel=-1E20), define_param("si_p", TypeLayer, "'Si P' (22/0)", default=TECHLAYERS["Si P"], layertype='doping', dopinglevel=5E17), define_param("si_ppp", TypeLayer, "'Si P++' (26/0)", default=TECHLAYERS["Si P++"], layertype='doping', dopinglevel=1E20), define_param("SEM", TypeLayer, "SEM (200/0)", default=TECHLAYERS["SEM"]), define_param("M1", TypeLayer, "M1 (41/0)", default=TECHLAYERS["M1"]), define_param("ML", TypeLayer, "'12_M2' (12/0)", default=TECHLAYERS["12_M2"]), define_param("ML_Open", TypeLayer, "'13_MLopen' (13/0)", default=TECHLAYERS["13_MLopen"]), define_param("VC", TypeLayer, "VC (40/0)", default=TECHLAYERS["VC"], layertype='contact'), define_param("M_Heater", TypeLayer, "'M Heater' (47/0)", default=TECHLAYERS["M Heater"]), define_param("devrec", TypeLayer, "DevRec (68/0)", default=TECHLAYERS["DevRec"]), define_param("pinrec", TypeLayer, "PinRec (1/10)", default=TECHLAYERS["PinRec"]), define_param("FbrTgt", TypeLayer, "FbrTgt (81/0)", default=TECHLAYERS["FbrTgt"]), define_param("Lumerical", TypeLayer, "Lumerical (733/0)", default=TECHLAYERS["Lumerical"]), define_param("sim_crossSection", TypeLayer, "Simulation cross section (1000/0)", default=TECHLAYERS["sim_crossSection"], layertype='sim', zmin=-0.75, zmax=1), ) def get_layers(self, layout): """ Instantiates layers into layout and returns a convenient objectview. Use with `lay = self.get_layers(layout); lay.Si` """ cp = self.get_cell_params() # breakpoint() lay = objectview({}) lay.Si = layout.layer(cp.silayer) lay.slab = layout.layer(cp.sislab) lay.SiN = layout.layer(cp.si_n) lay.SiNpp = layout.layer(cp.si_npp) lay.SiP = layout.layer(cp.si_p) lay.SiPpp = layout.layer(cp.si_ppp) lay.M1 = layout.layer(cp.M1) lay.ML = layout.layer(cp.ML) lay.VC = layout.layer(cp.VC) lay.M_Heater = layout.layer(cp.M_Heater) lay.PinRec = layout.layer(cp.pinrec) lay.DevRec = layout.layer(cp.devrec) lay.Text = layout.layer(cp.textl) lay.MLOpen = layout.layer(cp.ML_Open) return lay
def test_basic_usage(): pc = ParamContainer() pc.add_param(PCellParameter(name="orange", default=1)) assert pc.orange == 1 pc.orange = 2 assert pc.orange == 2 pc["orange"] == 2 # it is also accessible as a dictionary with pytest.raises(TypeError, match="Cannot set 'orange' to 'blah'"): pc.orange = "blah" with pytest.raises( RuntimeError, match="Unkown parameter type, cannot determine from default." ): pc.add_param(PCellParameter(name="apple")) pc.add_param(PCellParameter(name="apple", type=int)) with pytest.raises(TypeError, match="Cannot set 'apple' to 'one'"): pc.apple = "one" pc.add_param(PCellParameter(name="strawberry", default=DPoint(0, 0))) assert type(pc.strawberry) == DPoint with pytest.raises(TypeError, match="Cannot set 'strawberry' to 'test'"): pc.strawberry = "test"
class EBeamLayersMixin(PCell): """ Abstract class with more concise layer handling """ params = ParamContainer( define_param("silayer", TypeLayer, "Si (1/0)", default=TECHLAYERS["Si"]), define_param( "31_Si_p6nm", TypeLayer, "'31_Si_p6nm' (31/0)", default=TECHLAYERS["31_Si_p6nm"], ), define_param("textl", TypeLayer, "Text (10/0)", default=TECHLAYERS["Text"]), define_param("si_n", TypeLayer, "'Si N' (20/0)", default=TECHLAYERS["Si N"]), define_param("si_npp", TypeLayer, "'Si N++' (24/0)", default=TECHLAYERS["Si N++"]), define_param("SEM", TypeLayer, "SEM (200/0)", default=TECHLAYERS["SEM"]), define_param("M1", TypeLayer, "M1 (41/0)", default=TECHLAYERS["M1"]), define_param("ML", TypeLayer, "'12_M2' (12/0)", default=TECHLAYERS["12_M2"]), define_param("ML_Open", TypeLayer, "'13_MLopen' (13/0)", default=TECHLAYERS["13_MLopen"]), define_param("VC", TypeLayer, "VC (40/0)", default=TECHLAYERS["VC"]), define_param("M_Heater", TypeLayer, "'M Heater' (47/0)", default=TECHLAYERS["M Heater"]), define_param("devrec", TypeLayer, "DevRec (68/0)", default=TECHLAYERS["DevRec"]), define_param("pinrec", TypeLayer, "PinRec (1/10)", default=TECHLAYERS["PinRec"]), define_param("FbrTgt", TypeLayer, "FbrTgt (81/0)", default=TECHLAYERS["FbrTgt"]), define_param("Lumerical", TypeLayer, "Lumerical (733/0)", default=TECHLAYERS["Lumerical"]), ) def get_layers(self, layout): """ Instantiates layers into layout and returns a convenient objectview. Use with `lay = self.get_layers(layout); lay.Si` """ cp = self.get_cell_params() # breakpoint() lay = objectview({}) lay.Si = layout.layer(cp.silayer) lay.SiN = layout.layer(cp.si_n) lay.SiNpp = layout.layer(cp.si_npp) lay.M1 = layout.layer(cp.M1) lay.ML = layout.layer(cp.ML) lay.M_Heater = layout.layer(cp.M_Heater) lay.PinRec = layout.layer(cp.pinrec) lay.DevRec = layout.layer(cp.devrec) lay.Text = layout.layer(cp.textl) lay.MLOpen = layout.layer(cp.ML_Open) return lay