From 2293add29524f29f2f3d8ca40fd8f7093d4138e3 Mon Sep 17 00:00:00 2001 From: David Janzso Date: Fri, 22 Dec 2023 14:51:08 +0200 Subject: [PATCH] Etch opposite face feature If 'etch_opposite_face' parameter is true there will be a gap etched into the opposite face metal like the base metal gap shape in the first face but increased with 'etch_opposite_face_margin'. Issue #1347 --- .../chips/quality_factor_twoface.py | 32 ++++++++----------- .../python/kqcircuits/elements/element.py | 19 ++++++++++- .../elements/spiral_resonator_polygon.py | 4 +-- .../kqcircuits/simulations/simulation.py | 4 ++- .../kqcircuits/simulations/waveguides_sim.py | 4 +-- .../simulations/waveguides_sim_xsection.py | 2 +- .../xsection_cull_with_boundaries.py | 2 +- 7 files changed, 40 insertions(+), 27 deletions(-) diff --git a/klayout_package/python/kqcircuits/chips/quality_factor_twoface.py b/klayout_package/python/kqcircuits/chips/quality_factor_twoface.py index e2344eedf..382e7b4cb 100644 --- a/klayout_package/python/kqcircuits/chips/quality_factor_twoface.py +++ b/klayout_package/python/kqcircuits/chips/quality_factor_twoface.py @@ -48,9 +48,9 @@ class QualityFactorTwoface(Chip): cap_res_distance = Param(pdt.TypeDouble, "Distance between spiral resonator and capacitor", 200) waveguide_indentation = Param(pdt.TypeDouble, "Waveguide indentation from top chip edge", 500) extra_resonator_avoidance = Param(pdt.TypeList, "Added avoidance", [0, 0, 0, 0, 0, 0], unit="[μm]", - docstring="Added avoidance around resonators [μm]") - etch_opposite_face_margin = Param(pdt.TypeDouble, "Margin around the waveguide to etch on the opposite face " + - "for 'etched' type resonators", 5) + docstring="Added avoidance around resonators. At both faces.") + extra_resonator_etch = Param(pdt.TypeList, "Added opposite face etch", [0, 0, 0, 0, 0, 0], unit="μm", + docstring="Extra opposite side etching margin around resonators.") def build(self): self._produce_resonators() @@ -105,14 +105,15 @@ def _produce_resonators(self): # Create resonators for i in range(n_resonators): + extra_resonator_etch = float(self.extra_resonator_etch[i]) if i < len(self.extra_resonator_etch) else 0.0 self.produce_resonator(i, float(self.res_a[i]), float(self.res_b[i]), float(self.res_lengths[i]), float(self.n_fingers[i]), float(self.l_fingers[i]), self.type_coupler[i], resonator_face_ids, self.resonator_types[i], float(self.connector_distances[i]), - float(self.extra_resonator_avoidance[i]), mirror=(i % 2 == 1)) + float(self.extra_resonator_avoidance[i]), extra_resonator_etch, mirror=(i % 2 == 1)) def produce_resonator(self, i, a, b, length, n_fingers, l_fingers, type_coupler, face_ids, resonator_type="capped", connector_distance=0.0, - extra_resonator_avoidance=0.0, mirror=False): + extra_resonator_avoidance=0.0, extra_resonator_etch=0.0, mirror=False): """ Produce a single spiral resonator and corresponding input capacitor. @@ -132,6 +133,7 @@ def produce_resonator(self, i, a, b, length, n_fingers, l_fingers, type_coupler, resonator_type: String, type of resonator, one of ``etched``, ``capped``, ``solid`` or ``twoface`` connector_distance: For ``twoface`` resonators, distance of the flip chip connector starting from capacitor extra_resonator_avoidance: Extra ``ground_grid_avoidance`` margin around the resonator + extra_resonator_etch: Extra etching on top of ``etch_opposite_face_margin`` mirror: Turn clockwise if False, or counter-clockwise if True. """ @@ -151,7 +153,8 @@ def produce_resonator(self, i, a, b, length, n_fingers, l_fingers, type_coupler, cplr_params = cap_params( n_fingers, l_fingers, type_coupler, protect_opposite_face=protect_opposite_face, face_ids=face_ids, a=self.a_capped, b=self.b_capped, a2=a, b2=b, element_key='cell') - inst_cplr, cplr_refpoints = self.insert_cell(**cplr_params, align="port_a", align_to=start, + _, cplr_refpoints = self.insert_cell(**cplr_params, align="port_a", align_to=start, + etch_opposite_face=(resonator_type == "etched"), trans=pya.DCplxTrans(1, start_angle, False, 0, 0)) # Spiral resonator @@ -161,15 +164,17 @@ def produce_resonator(self, i, a, b, length, n_fingers, l_fingers, type_coupler, res_params = { "airbridge_type": "Airbridge Multi Face", "include_bumps": False, - "bridge_length": a + 2 * (b + self.etch_opposite_face_margin + extra_resonator_avoidance), + "bridge_length": a + 2 * (b + self.etch_opposite_face_margin + extra_resonator_etch), "bridge_width": 2, "pad_length": 2, "bridge_spacing": self.bridge_spacing, + "etch_opposite_face": True, + "etch_opposite_face_margin": self.etch_opposite_face_margin + extra_resonator_etch, } else: res_params = {"bridge_spacing": 0} - inst_res, _ = self.insert_cell( + self.insert_cell( SpiralResonatorPolygon, margin=self.margin + extra_resonator_avoidance, input_path=pya.DPath([ @@ -190,14 +195,3 @@ def produce_resonator(self, i, a, b, length, n_fingers, l_fingers, type_coupler, inst_name=f'resonator{i}', trans=pya.DCplxTrans(1, start_angle, mirror, cplr_refpoints["port_b"]) ) - - # Top chip etching and grid avoidance above resonator - if resonator_type == "etched": - l0 = self.get_layer("ground_grid_avoidance", int(self.resonator_faces[0])) - region = pya.Region(inst_res.cell.begin_shapes_rec(l0)).transformed(inst_res.trans) - region += pya.Region(inst_cplr.cell.begin_shapes_rec(l0)).transformed(inst_cplr.trans) - region = region.sized((self.etch_opposite_face_margin - self.margin) / self.layout.dbu) - protection_region = region.sized(self.margin / self.layout.dbu) - opposite_face = int(self.resonator_faces[1]) - self.cell.shapes(self.get_layer("ground_grid_avoidance", opposite_face)).insert(protection_region) - self.cell.shapes(self.get_layer("base_metal_gap_wo_grid", opposite_face)).insert(region) diff --git a/klayout_package/python/kqcircuits/elements/element.py b/klayout_package/python/kqcircuits/elements/element.py index ff7085b9b..8133ff314 100644 --- a/klayout_package/python/kqcircuits/elements/element.py +++ b/klayout_package/python/kqcircuits/elements/element.py @@ -121,6 +121,8 @@ class Element(pya.PCellDeclarationHelper): "metal between gaps.") opposing_face_id_groups = Param(pdt.TypeList, "Opposing face ID groups (list of lists)", [["1t1", "2b1"]], hidden=True) + etch_opposite_face = Param(pdt.TypeBoolean, "Etch avoidance shaped gap on the opposite face too", False) + etch_opposite_face_margin = Param(pdt.TypeDouble, "Margin of the opposite face etch shape", 5, unit="μm") def __init__(self): "" @@ -409,11 +411,26 @@ def produce_impl(self): text = pya.DText(name, refpoint.x, refpoint.y) self.cell.shapes(self.get_layer("refpoints")).insert(text) + def _etch_opposite_face(self): + """Add opposite face etching, if enabled.""" + if self.etch_opposite_face: + etch_shape = pya.Region(self.cell.begin_shapes_rec(self.get_layer("ground_grid_avoidance"))).merged() + etch_shape.size((self.etch_opposite_face_margin - self.margin) / self.layout.dbu) + protection = etch_shape.sized(self.margin / self.layout.dbu) + face = self.face_ids[0] + for group in self.opposing_face_id_groups: + if face in group: + for other_face in group: + if other_face != face: + self.cell.shapes(self.get_layer("base_metal_gap_wo_grid", other_face)).insert(etch_shape) + self.cell.shapes(self.get_layer("ground_grid_avoidance", other_face)).insert(protection) + def build(self): """Child classes re-define this method to build the PCell.""" def post_build(self): - """Child classes re-define this method for post-build operations""" + """Child classes may re-define this method for post-build operations.""" + self._etch_opposite_face() def display_text_impl(self): if self.display_name: diff --git a/klayout_package/python/kqcircuits/elements/spiral_resonator_polygon.py b/klayout_package/python/kqcircuits/elements/spiral_resonator_polygon.py index c1a445c3b..f508e6d94 100644 --- a/klayout_package/python/kqcircuits/elements/spiral_resonator_polygon.py +++ b/klayout_package/python/kqcircuits/elements/spiral_resonator_polygon.py @@ -112,7 +112,6 @@ def polygon_min_diameter(path): else: max_spacing = spacing self._produce_resonator(optimal_points) - self.add_port("a", optimal_points[0], optimal_points[0] - optimal_points[1]) def _produce_resonator_manual_spacing(self): """Produces polygon spiral resonator with spacing defined by `self.manual_spacing`. @@ -125,7 +124,6 @@ def _produce_resonator_manual_spacing(self): self.raise_error_on_cell("Cannot create a resonator with the given parameters. Try decreasing the spacings " "or the turn radius.", (self.input_path.bbox() + self.poly_path.bbox()).center()) self._produce_resonator(points) - self.add_port("a", points[0], points[0] - points[1]) def _produce_path_points(self, spacing): """Creates resonator path points with the given spacing. @@ -280,6 +278,8 @@ def _produce_resonator(self, points): else: self.insert_cell(WaveguideCoplanar, path=points, term2=term2) + self.add_port("a", points[0], points[0] - points[1]) + def _fix_waveguide_end(self, points, current_length): """Modifies the last points and places a WaveguideCoplanarCurved element at the end if needed. diff --git a/klayout_package/python/kqcircuits/simulations/simulation.py b/klayout_package/python/kqcircuits/simulations/simulation.py index 00e92b3f9..11b2f53ba 100644 --- a/klayout_package/python/kqcircuits/simulations/simulation.py +++ b/klayout_package/python/kqcircuits/simulations/simulation.py @@ -696,7 +696,7 @@ def produce_waveguide_to_port(self, location, towards, port_nr, side=None, use_internal_ports=None, waveguide_length=None, term1=0, turn_radius=None, a=None, b=None, over_etching=None, - airbridge=False, face=0): + airbridge=False, face=0, etch_opposite_face=False): """Create a waveguide connection from some `location` to a port, and add the corresponding port to `simulation.ports`. @@ -793,6 +793,7 @@ def produce_waveguide_to_port(self, location, towards, port_nr, side=None, term2=0, a=waveguide_a, b=waveguide_b, + etch_opposite_face=etch_opposite_face, face_ids=[self.face_ids[face]] ) @@ -806,6 +807,7 @@ def produce_waveguide_to_port(self, location, towards, port_nr, side=None, b=b, term1=a-4*over_etching, term2=0, + etch_opposite_face=etch_opposite_face, face_ids=[self.face_ids[face]] ) self.cell.insert(pya.DCellInstArray(port_end_piece.cell_index(), pya.DTrans())) diff --git a/klayout_package/python/kqcircuits/simulations/waveguides_sim.py b/klayout_package/python/kqcircuits/simulations/waveguides_sim.py index f82904d30..d7107b93f 100644 --- a/klayout_package/python/kqcircuits/simulations/waveguides_sim.py +++ b/klayout_package/python/kqcircuits/simulations/waveguides_sim.py @@ -32,7 +32,7 @@ class WaveGuidesSim(Simulation): add_bumps = Param(pdt.TypeBoolean, "Add ground bumps", False) port_termination_end = Param(pdt.TypeBoolean, "Port termination end", True) use_edge_ports = Param(pdt.TypeBoolean, "Use edge ports", True) - etch_opposite_face = Param(pdt.TypeBoolean, "Remove the whole opposite face metal if flip chip", False) + etch_whole_opposite_face = Param(pdt.TypeBoolean, "Remove the whole opposite face metal if flip chip", False) def build(self): @@ -80,7 +80,7 @@ def produce_guides(self): term2=self.b, face_ids=[guide_face_id]) self.insert_cell(wg_cell) - if self.etch_opposite_face: + if self.etch_whole_opposite_face: region = pya.Region(self.box.to_itype(self.layout.dbu)) self.cell.shapes(self.get_layer("base_metal_gap_wo_grid", face_id=1)).insert(region) diff --git a/klayout_package/python/scripts/simulations/waveguides_sim_xsection.py b/klayout_package/python/scripts/simulations/waveguides_sim_xsection.py index f5173c191..0f66c3439 100644 --- a/klayout_package/python/scripts/simulations/waveguides_sim_xsection.py +++ b/klayout_package/python/scripts/simulations/waveguides_sim_xsection.py @@ -63,7 +63,7 @@ 'box': pya.DBox(pya.DPoint(-cpw_length/2., -sim_box_height/2.), pya.DPoint(cpw_length/2., sim_box_height/2.)), 'cpw_length': cpw_length, 'face_stack': ['1t1', '2b1'] if multiface else ['1t1'], - 'etch_opposite_face': args.etch_opposite_face, + 'etch_whole_opposite_face': args.etch_whole_opposite_face, } workflow = { diff --git a/klayout_package/python/scripts/simulations/xsection_cull_with_boundaries.py b/klayout_package/python/scripts/simulations/xsection_cull_with_boundaries.py index fff17a59c..58afc6582 100644 --- a/klayout_package/python/scripts/simulations/xsection_cull_with_boundaries.py +++ b/klayout_package/python/scripts/simulations/xsection_cull_with_boundaries.py @@ -59,7 +59,7 @@ 'box': pya.DBox(pya.DPoint(-cpw_length/2., -sim_box_height/2.), pya.DPoint(cpw_length/2., sim_box_height/2.)), 'cpw_length': cpw_length, 'face_stack': ['1t1', '2b1'] if multiface else ['1t1'], - 'etch_opposite_face': args.etch_opposite_face, + 'etch_whole_opposite_face': args.etch_whole_opposite_face, 'n_guides': 1, }