Coverage for qpdk / cells / derived / transmon_with_resonator_and_probeline.py: 99%
68 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-14 10:27 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-14 10:27 +0000
1"""Transmons with resonators coupled."""
3from __future__ import annotations
5import uuid
6from functools import partial
8import gdsfactory as gf
9from gdsfactory.component import Component
10from gdsfactory.typings import ComponentSpec, CrossSectionSpec
11from klayout.db import DCplxTrans
13from qpdk.cells.capacitor import plate_capacitor_single
14from qpdk.cells.resonator import (
15 resonator_quarter_wave_bend_start,
16)
17from qpdk.cells.waveguides import coupler_straight
18from qpdk.helper import show_components
19from qpdk.tech import route_bundle_cpw
22def _transmon_with_resonator_base(
23 qubit: ComponentSpec = "double_pad_transmon_with_bbox",
24 resonator: ComponentSpec = partial(
25 resonator_quarter_wave_bend_start, length=4000, meanders=6
26 ),
27 resonator_meander_start: tuple[float, float] = (-700, -1300),
28 resonator_length: float = 5000.0,
29 resonator_meanders: int = 5,
30 resonator_bend_spec: ComponentSpec = "bend_circular",
31 resonator_cross_section: CrossSectionSpec = "cpw",
32 resonator_open_start: bool = False,
33 resonator_open_end: bool = True,
34 coupler: ComponentSpec = partial(plate_capacitor_single, width=20, length=394),
35 qubit_rotation: float = 90,
36 coupler_port: str = "left_pad",
37 coupler_offset: tuple[float, float] = (-45, 0),
38 with_probeline: bool = True,
39 probeline_coupler: ComponentSpec = coupler_straight,
40 probeline_coupling_gap: float = 16.0,
41 probeline_coupling_length: float | None = None,
42) -> Component:
43 """Base function for creating a transmon coupled to a resonator, optionally with a probeline.
45 Args:
46 qubit: Qubit component.
47 resonator: Resonator component.
48 resonator_meander_start: (x, y) position of the start of the resonator meander.
49 resonator_length: Length of the resonator in µm.
50 resonator_meanders: Number of meander sections for the resonator.
51 resonator_bend_spec: Specification for the bend component used in meanders.
52 resonator_cross_section: Cross-section specification for the resonator.
53 resonator_open_start: If True, adds an etch section at the start of the resonator.
54 resonator_open_end: If True, adds an etch section at the end of the resonator.
55 coupler: Coupler spec.
56 qubit_rotation: Rotation angle for the qubit in degrees.
57 coupler_port: Name of the qubit port to position the coupler relative to.
58 coupler_offset: (x, y) offset for the coupler position.
59 with_probeline: Whether to include a probeline coupler.
60 probeline_coupler: Component spec for the probeline coupling section.
61 probeline_coupling_gap: Gap between the resonator and probeline
62 waveguides in the coupling section in µm.
63 probeline_coupling_length: Length of the coupling section in µm.
64 If None, it will be calculated based on the distance from the
65 resonator meander start to the coupler position.
66 """
67 c = Component()
69 qubit_ref = c << gf.get_component(qubit)
70 qubit_ref.rotate(qubit_rotation)
71 coupler_ref = c << gf.get_component(coupler)
73 # Position coupler close to qubit
74 coupler_ref.transform(
75 qubit_ref.ports[coupler_port].dcplx_trans
76 * DCplxTrans.R180
77 * DCplxTrans(*coupler_offset)
78 )
80 coupling_o1_port = None
81 coupling_o2_port = None
83 if with_probeline:
84 if probeline_coupling_length is None:
85 xs = gf.get_cross_section(resonator_cross_section)
86 radius = getattr(xs, "radius", 100.0)
87 probeline_coupling_length = (
88 abs(resonator_meander_start[0] - coupler_ref.ports["o1"].x) - radius
89 )
91 if probeline_coupling_length <= 0:
92 raise ValueError(
93 f"Auto-computed probeline_coupling_length={probeline_coupling_length:.1f} µm is non-positive. "
94 "Increase the distance between resonator_meander_start and the coupler, or pass an explicit probeline_coupling_length."
95 )
97 # Create probeline coupling.
98 cs_ref = c << gf.get_component(
99 probeline_coupler,
100 gap=probeline_coupling_gap,
101 length=probeline_coupling_length,
102 cross_section=resonator_cross_section,
103 )
105 # Position the coupler_straight so that o2 (bottom left) is at
106 # the resonator_meander_start position.
107 cs_ref.move(
108 (
109 resonator_meander_start[0] - cs_ref.ports["o2"].x,
110 resonator_meander_start[1] - cs_ref.ports["o2"].y,
111 )
112 )
113 route_start_port = cs_ref.ports["o3"]
114 resonator_connect_port = cs_ref.ports["o2"]
115 coupling_o1_port = cs_ref.ports["o1"]
116 coupling_o2_port = cs_ref.ports["o4"]
117 added_length = probeline_coupling_length
118 else:
119 # We need a port to start the route from, so we create a dummy port
120 dummy = gf.Component(name=f"dummy_route_start_{uuid.uuid4().hex[:8]}")
121 dummy.add_port(
122 name="o1",
123 center=(0, 0),
124 width=10,
125 orientation=180,
126 cross_section=resonator_cross_section,
127 )
128 dummy_port = dummy.ports["o1"].copy()
129 dummy_port.center = resonator_meander_start
130 dummy_port.orientation = 0
131 route_start_port = dummy_port
132 resonator_connect_port = None
133 added_length = 0.0
135 # Route from meander start to the plate capacitor
136 routes = route_bundle_cpw(
137 component=c,
138 ports1=[route_start_port],
139 ports2=[coupler_ref.ports["o1"]],
140 auto_taper=False,
141 )
142 route = routes[0]
144 # Create resonator, accounting for both the route and coupling lengths
145 resonator_ref = c << gf.get_component(
146 resonator,
147 length=resonator_length - route.length * c.kcl.dbu - added_length,
148 meanders=resonator_meanders,
149 bend_spec=resonator_bend_spec,
150 cross_section=resonator_cross_section,
151 open_start=resonator_open_start,
152 open_end=resonator_open_end,
153 )
155 if with_probeline:
156 # Connect the resonator's shorted end to the coupler_straight's top-left port.
157 resonator_ref.connect("o1", resonator_connect_port)
158 else:
159 # Connect to the start of the route
160 resonator_ref.connect("o1", route.instances[0].ports["o1"])
162 c.info["qubit_type"] = qubit_ref.cell.info.get("qubit_type")
163 c.info["resonator_type"] = resonator_ref.cell.info.get("resonator_type")
164 c.info["coupler_type"] = coupler_ref.cell.info.get("coupler_type")
165 c.info["length"] = (
166 resonator_ref.cell.info.get("length") + route.length * c.kcl.dbu + added_length
167 )
169 c.add_ports(qubit_ref.ports.filter(regex=r"junction"))
170 if with_probeline and coupling_o1_port:
171 c.add_port("coupling_o1", port=coupling_o1_port)
172 c.add_port("coupling_o2", port=coupling_o2_port)
173 c.add_port(
174 port=resonator_ref.ports["o1"],
175 port_type="placement",
176 )
177 return c
180@gf.cell
181def transmon_with_resonator_and_probeline(
182 qubit: ComponentSpec = "double_pad_transmon_with_bbox",
183 resonator: ComponentSpec = partial(
184 resonator_quarter_wave_bend_start, length=4000, meanders=6
185 ),
186 resonator_meander_start: tuple[float, float] = (-900, -1200),
187 resonator_length: float = 5000.0,
188 resonator_meanders: int = 5,
189 resonator_bend_spec: ComponentSpec = "bend_circular",
190 resonator_cross_section: CrossSectionSpec = "cpw",
191 resonator_open_start: bool = False,
192 resonator_open_end: bool = True,
193 coupler: ComponentSpec = partial(plate_capacitor_single, width=20, length=394),
194 qubit_rotation: float = 90,
195 coupler_port: str = "left_pad",
196 coupler_offset: tuple[float, float] = (-45, 0),
197 probeline_coupler: ComponentSpec = coupler_straight,
198 probeline_coupling_gap: float = 16.0,
199 probeline_coupling_length: float | None = None,
200) -> Component:
201 """Returns a transmon qubit coupled to a quarter wave resonator and a probeline.
203 Uses a :func:`~qpdk.cells.waveguides.coupler_straight` to couple the
204 resonator to a probeline. The coupling section is inserted at the start
205 of the resonator meander, with one waveguide carrying the resonator signal
206 and the other providing ports for probeline routing.
208 Args:
209 qubit: Qubit component.
210 resonator: Resonator component.
211 resonator_meander_start: (x, y) position of the start of the resonator meander.
212 resonator_length: Length of the resonator in µm.
213 resonator_meanders: Number of meander sections for the resonator.
214 resonator_bend_spec: Specification for the bend component used in meanders.
215 resonator_cross_section: Cross-section specification for the resonator.
216 resonator_open_start: If True, adds an etch section at the start of the resonator.
217 resonator_open_end: If True, adds an etch section at the end of the resonator.
218 coupler: Coupler spec.
219 qubit_rotation: Rotation angle for the qubit in degrees.
220 coupler_port: Name of the qubit port to position the coupler relative to.
221 coupler_offset: (x, y) offset for the coupler position.
222 probeline_coupler: Component spec for the probeline coupling section.
223 probeline_coupling_gap: Gap between the resonator and probeline
224 waveguides in the coupling section in µm.
225 probeline_coupling_length: Length of the coupling section in µm.
226 """
227 return _transmon_with_resonator_base(
228 qubit=qubit,
229 resonator=resonator,
230 resonator_meander_start=resonator_meander_start,
231 resonator_length=resonator_length,
232 resonator_meanders=resonator_meanders,
233 resonator_bend_spec=resonator_bend_spec,
234 resonator_cross_section=resonator_cross_section,
235 resonator_open_start=resonator_open_start,
236 resonator_open_end=resonator_open_end,
237 coupler=coupler,
238 qubit_rotation=qubit_rotation,
239 coupler_port=coupler_port,
240 coupler_offset=coupler_offset,
241 with_probeline=True,
242 probeline_coupler=probeline_coupler,
243 probeline_coupling_gap=probeline_coupling_gap,
244 probeline_coupling_length=probeline_coupling_length,
245 )
248@gf.cell
249def transmon_with_resonator(
250 qubit: ComponentSpec = "double_pad_transmon_with_bbox",
251 resonator: ComponentSpec = partial(
252 resonator_quarter_wave_bend_start, length=4000, meanders=6
253 ),
254 resonator_meander_start: tuple[float, float] = (-900, -1200),
255 resonator_length: float = 5000.0,
256 resonator_meanders: int = 5,
257 resonator_bend_spec: ComponentSpec = "bend_circular",
258 resonator_cross_section: CrossSectionSpec = "cpw",
259 resonator_open_start: bool = False,
260 resonator_open_end: bool = True,
261 coupler: ComponentSpec = partial(plate_capacitor_single, width=20, length=394),
262 qubit_rotation: float = 90,
263 coupler_port: str = "left_pad",
264 coupler_offset: tuple[float, float] = (-45, 0),
265) -> Component:
266 """Returns a transmon qubit coupled to a quarter wave resonator.
268 Args:
269 qubit: Qubit component.
270 resonator: Resonator component.
271 resonator_meander_start: (x, y) position of the start of the resonator meander.
272 resonator_length: Length of the resonator in µm.
273 resonator_meanders: Number of meander sections for the resonator.
274 resonator_bend_spec: Specification for the bend component used in meanders.
275 resonator_cross_section: Cross-section specification for the resonator.
276 resonator_open_start: If True, adds an etch section at the start of the resonator.
277 resonator_open_end: If True, adds an etch section at the end of the resonator.
278 coupler: Coupler spec.
279 qubit_rotation: Rotation angle for the qubit in degrees.
280 coupler_port: Name of the qubit port to position the coupler relative to.
281 coupler_offset: (x, y) offset for the coupler position.
282 """
283 return _transmon_with_resonator_base(
284 qubit=qubit,
285 resonator=resonator,
286 resonator_meander_start=resonator_meander_start,
287 resonator_length=resonator_length,
288 resonator_meanders=resonator_meanders,
289 resonator_bend_spec=resonator_bend_spec,
290 resonator_cross_section=resonator_cross_section,
291 resonator_open_start=resonator_open_start,
292 resonator_open_end=resonator_open_end,
293 coupler=coupler,
294 qubit_rotation=qubit_rotation,
295 coupler_port=coupler_port,
296 coupler_offset=coupler_offset,
297 with_probeline=False,
298 )
301# Create specific functions as partials of the general function
302double_pad_transmon_with_resonator_and_probeline = partial(
303 transmon_with_resonator_and_probeline,
304 qubit="double_pad_transmon_with_bbox",
305 coupler=partial(plate_capacitor_single, width=20, length=394),
306 qubit_rotation=90,
307 coupler_port="left_pad",
308 coupler_offset=(-45, 0),
309)
311flipmon_with_resonator_and_probeline = partial(
312 transmon_with_resonator_and_probeline,
313 qubit="flipmon_with_bbox",
314 coupler=partial(plate_capacitor_single, width=10, length=58),
315 qubit_rotation=-90,
316 coupler_port="outer_ring_outside",
317 coupler_offset=(-10, 0),
318)
320double_pad_transmon_with_resonator = partial(
321 transmon_with_resonator,
322 qubit="double_pad_transmon_with_bbox",
323 coupler=partial(plate_capacitor_single, width=20, length=394),
324 qubit_rotation=90,
325 coupler_port="left_pad",
326 coupler_offset=(-45, 0),
327)
329flipmon_with_resonator = partial(
330 transmon_with_resonator,
331 qubit="flipmon_with_bbox",
332 coupler=partial(plate_capacitor_single, width=10, length=58),
333 qubit_rotation=-90,
334 coupler_port="outer_ring_outside",
335 coupler_offset=(-10, 0),
336)
339if __name__ == "__main__":
340 show_components(
341 transmon_with_resonator_and_probeline,
342 double_pad_transmon_with_resonator_and_probeline,
343 flipmon_with_resonator_and_probeline,
344 transmon_with_resonator,
345 double_pad_transmon_with_resonator,
346 flipmon_with_resonator,
347 )