Coverage for qpdk / cells / transmon.py: 100%

109 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-14 10:27 +0000

1"""Transmon qubit components.""" 

2 

3from __future__ import annotations 

4 

5import operator 

6from functools import partial, reduce 

7 

8import gdsfactory as gf 

9from gdsfactory.component import Component 

10from gdsfactory.typings import ComponentSpec, LayerSpec 

11from kfactory import kdb 

12from klayout.db import DCplxTrans, Region 

13 

14from qpdk.cells.bump import indium_bump 

15from qpdk.cells.helpers import transform_component 

16from qpdk.cells.junction import squid_junction, squid_junction_long 

17from qpdk.helper import show_components 

18from qpdk.tech import LAYER 

19 

20 

21@gf.cell(check_instances=False) 

22def double_pad_transmon( 

23 pad_size: tuple[float, float] = (250.0, 400.0), 

24 pad_gap: float = 15.0, 

25 junction_spec: ComponentSpec = squid_junction, 

26 junction_displacement: DCplxTrans | None = None, 

27 layer_metal: LayerSpec = LAYER.M1_DRAW, 

28) -> Component: 

29 """Creates a double capacitor pad transmon qubit with Josephson junction. 

30 

31 A transmon qubit consists of two capacitor pads connected by a Josephson junction. 

32 The junction creates an anharmonic oscillator that can be used as a qubit. 

33 

34 .. svgbob:: 

35 

36 +---------+ +---------+ 

37 | | | | 

38 | pad1 | XX | pad2 | 

39 | | XX | | 

40 +---------+ junct. +---------+ 

41 left_pad right_pad 

42 

43 See :cite:`kochChargeinsensitiveQubitDesign2007a` for details. 

44 

45 Args: 

46 pad_size: (width, height) of each capacitor pad in μm. 

47 pad_gap: Gap between the two capacitor pads in μm. 

48 junction_spec: Component specification for the Josephson junction component. 

49 junction_displacement: Optional complex transformation to apply to the junction. 

50 layer_metal: Layer for the metal pads. 

51 

52 Returns: 

53 Component: A gdsfactory component with the transmon geometry. 

54 """ 

55 c = Component() 

56 

57 pad_width, pad_height = pad_size 

58 

59 def create_capacitor_pad(x_offset: float) -> gf.ComponentReference: 

60 pad = gf.components.rectangle( 

61 size=pad_size, 

62 layer=layer_metal, 

63 ) 

64 pad_ref = c.add_ref(pad) 

65 pad_ref.move((x_offset, -pad_height / 2)) 

66 return pad_ref 

67 

68 create_capacitor_pad(-pad_width - pad_gap / 2) 

69 create_capacitor_pad(pad_gap / 2) 

70 

71 # Create Josephson junction 

72 junction_ref = c.add_ref(gf.get_component(junction_spec)) 

73 junction_ref.rotate(45) 

74 # Center the junction between the pads 

75 junction_ref.dcenter = c.dcenter # move((-junction_height / 2, 0)) 

76 if junction_displacement: 

77 junction_ref.transform(junction_displacement) 

78 

79 # Add ports for easy reference 

80 ports_config = [ 

81 { 

82 "name": "left_pad", 

83 "center": (-pad_width - pad_gap / 2, 0), 

84 "width": pad_height, 

85 "orientation": 180, 

86 "layer": layer_metal, 

87 }, 

88 { 

89 "name": "left_pad_inner", 

90 "center": (-pad_gap / 2, 0), 

91 "width": pad_height, 

92 "orientation": 0, 

93 "layer": layer_metal, 

94 "port_type": "placement", 

95 }, 

96 { 

97 "name": "right_pad", 

98 "center": (pad_width + pad_gap / 2, 0), 

99 "width": pad_height, 

100 "orientation": 0, 

101 "layer": layer_metal, 

102 }, 

103 { 

104 "name": "right_pad_inner", 

105 "center": (pad_gap / 2, 0), 

106 "width": pad_height, 

107 "orientation": 180, 

108 "layer": layer_metal, 

109 "port_type": "placement", 

110 }, 

111 { 

112 "name": "junction", 

113 "center": junction_ref.dcenter, 

114 "width": junction_ref.size_info.height, 

115 "orientation": 90, 

116 "layer": LAYER.JJ_AREA, 

117 "port_type": "placement", 

118 }, 

119 ] 

120 for port_config in ports_config: 

121 c.add_port(**port_config) 

122 

123 # Add metadata 

124 c.info["qubit_type"] = "transmon" 

125 

126 return c 

127 

128 

129@gf.cell 

130def double_pad_transmon_with_bbox( 

131 bbox_extension: float = 200.0, 

132 pad_size: tuple[float, float] = (250.0, 400.0), 

133 pad_gap: float = 15.0, 

134 junction_spec: ComponentSpec = squid_junction, 

135 junction_displacement: DCplxTrans | None = None, 

136 layer_metal: LayerSpec = LAYER.M1_DRAW, 

137) -> Component: 

138 """Creates a double capacitor pad transmon qubit with Josephson junction and an etched bounding box. 

139 

140 See :func:`~double_pad_transmon` for more details. 

141 

142 Args: 

143 bbox_extension: Extension size for the bounding box in μm. 

144 pad_size: (width, height) of each capacitor pad in μm. 

145 pad_gap: Gap between the two capacitor pads in μm. 

146 junction_spec: Component specification for the Josephson junction component. 

147 junction_displacement: Optional complex transformation to apply to the junction. 

148 layer_metal: Layer for the metal pads. 

149 

150 Returns: 

151 Component: A gdsfactory component with the transmon geometry and etched box. 

152 """ 

153 c = gf.Component() 

154 double_pad_ref = c << double_pad_transmon( 

155 pad_size=pad_size, 

156 pad_gap=pad_gap, 

157 junction_spec=junction_spec, 

158 junction_displacement=junction_displacement, 

159 layer_metal=layer_metal, 

160 ) 

161 double_pad_size = (double_pad_ref.size_info.width, double_pad_ref.size_info.height) 

162 bbox_size = ( 

163 double_pad_size[0] + 2 * bbox_extension, 

164 double_pad_size[1] + 2 * bbox_extension, 

165 ) 

166 

167 bbox = gf.container( 

168 partial( 

169 gf.components.rectangle, 

170 size=bbox_size, 

171 layer=LAYER.M1_ETCH, 

172 ), 

173 # Center the bbox around the double pad 

174 partial( 

175 transform_component, transform=DCplxTrans(*(-e / 2 for e in bbox_size)) 

176 ), 

177 ) 

178 # Remove additive metal from etch 

179 bbox = gf.boolean( 

180 A=bbox, 

181 B=c, 

182 operation="-", 

183 layer=LAYER.M1_ETCH, 

184 layer1=LAYER.M1_ETCH, 

185 layer2=LAYER.M1_DRAW, 

186 ) 

187 bbox_ref = c.add_ref(bbox) 

188 c.absorb(bbox_ref) 

189 

190 c.add_ports(double_pad_ref.ports) 

191 return c 

192 

193 

194@gf.cell(check_instances=False) 

195def flipmon( 

196 inner_circle_radius: float = 60.0, 

197 outer_ring_radius: float = 110.0, 

198 outer_ring_width: float = 60.0, 

199 top_circle_radius: float = 110.0, 

200 junction_spec: ComponentSpec = squid_junction_long, 

201 junction_displacement: DCplxTrans | None = None, 

202 layer_metal: LayerSpec = LAYER.M1_DRAW, 

203 layer_metal_top: LayerSpec = LAYER.M2_DRAW, 

204) -> Component: 

205 """Creates a circular transmon qubit with `flipmon` geometry. 

206 

207 A circular variant of the transmon qubit with another circle as the inner pad. 

208 

209 See :cite:`liVacuumgapTransmonQubits2021,liCosmicrayinducedCorrelatedErrors2025` 

210 for details about the `flipmon` design. 

211 

212 Args: 

213 inner_circle_radius: Central radius of the inner circular capacitor pad in μm. 

214 outer_ring_radius: Central radius of the outer circular capacitor pad in μm. 

215 outer_ring_width: Width of the outer circular capacitor pad in μm. 

216 top_circle_radius: Central radius of the top circular capacitor pad in μm. 

217 There is no separate width as the filled circle is not a ring. 

218 junction_spec: Component specification for the Josephson junction component. 

219 junction_displacement: Optional complex transformation to apply to the junction. 

220 layer_metal: Layer for the metal pads. 

221 layer_metal_top: Layer for the other metal layer pad for flip-chip. 

222 

223 Returns: 

224 Component: A gdsfactory component with the circular transmon geometry. 

225 """ 

226 c = Component() 

227 

228 _ = c << gf.c.circle( 

229 radius=inner_circle_radius, 

230 layer=layer_metal, 

231 ) 

232 

233 _ = c << gf.c.ring( 

234 radius=outer_ring_radius, 

235 width=outer_ring_width, 

236 layer=layer_metal, 

237 ) 

238 

239 # Create Josephson junction 

240 junction_ref = c.add_ref(gf.get_component(junction_spec)) 

241 # Center the junction between the pads 

242 junction_ref.dcenter = c.dcenter # move((-junction_height / 2, 0)) 

243 junction_ref.dcplx_trans *= reduce( 

244 operator.mul, 

245 ( 

246 DCplxTrans(1, 45, False, 0, 0), 

247 DCplxTrans( 

248 (inner_circle_radius + outer_ring_radius - outer_ring_width / 2) / 2, 

249 0, 

250 ), 

251 DCplxTrans(0, -junction_ref.size_info.height), 

252 ), 

253 ) 

254 junction_ref.y = 0 

255 

256 if junction_displacement: 

257 junction_ref.transform(junction_displacement) 

258 

259 # Create top circular pad for flip-chip 

260 top_circle = gf.components.circle( 

261 radius=top_circle_radius, 

262 layer=layer_metal_top, 

263 ) 

264 top_circle_ref = c.add_ref(top_circle) 

265 top_circle_ref.dcenter = c.dcenter 

266 

267 # Add indium bump to flip-chip 

268 bump = gf.get_component(indium_bump) 

269 bump_ref = c.add_ref(bump) 

270 bump_ref.dcenter = c.dcenter 

271 c.add_ports(bump_ref.ports) 

272 

273 # Add ports for connections 

274 c.add_port( 

275 name="inner_ring_near_junction", 

276 center=(inner_circle_radius / 2, 0), 

277 width=outer_ring_width, 

278 orientation=0, 

279 layer=layer_metal, 

280 port_type="placement", 

281 ) 

282 c.add_port( 

283 name="outer_ring_near_junction", 

284 center=(outer_ring_radius - outer_ring_width / 2, 0), 

285 width=outer_ring_width, 

286 orientation=180, 

287 layer=layer_metal, 

288 port_type="placement", 

289 ) 

290 c.add_port( 

291 name="outer_ring_outside", 

292 center=(outer_ring_radius + outer_ring_width / 2, 0), 

293 width=outer_ring_width, 

294 orientation=0, 

295 layer=layer_metal, 

296 ) 

297 c.add_port( 

298 name="junction", 

299 center=junction_ref.dcenter, 

300 width=junction_ref.size_info.height, 

301 orientation=90, 

302 layer=LAYER.JJ_AREA, 

303 port_type="placement", 

304 ) 

305 

306 return c 

307 

308 

309@gf.cell 

310def flipmon_with_bbox( 

311 inner_circle_radius: float = 60.0, 

312 outer_ring_radius: float = 110.0, 

313 outer_ring_width: float = 60.0, 

314 top_circle_radius: float = 110.0, 

315 junction_spec: ComponentSpec = squid_junction_long, 

316 junction_displacement: DCplxTrans | None = None, 

317 layer_metal: LayerSpec = LAYER.M1_DRAW, 

318 layer_metal_top: LayerSpec = LAYER.M2_DRAW, 

319 m1_etch_extension_gap: float = 30.0, 

320 m2_etch_extension_gap: float = 40.0, 

321) -> Component: 

322 """Creates a circular transmon qubit with `flipmon` geometry and a circular etched bounding box. 

323 

324 See :func:`~flipmon` for more details. 

325 

326 Args: 

327 inner_circle_radius: Central radius of the inner circular capacitor pad in μm. 

328 outer_ring_radius: Central radius of the outer circular capacitor pad in μm. 

329 outer_ring_width: Width of the outer circular capacitor pad in μm. 

330 top_circle_radius: Central radius of the top circular capacitor pad in μm. 

331 junction_spec: Component specification for the Josephson junction component. 

332 junction_displacement: Optional complex transformation to apply to the junction. 

333 layer_metal: Layer for the metal pads. 

334 layer_metal_top: Layer for the other metal layer pad for flip-chip. 

335 m1_etch_extension_gap: Radius extension length for the M1 etch bounding box in μm. 

336 m2_etch_extension_gap: Radius extension length for the M2 etch bounding box in μm. 

337 

338 Returns: 

339 Component: A gdsfactory component with the flipmon geometry and etched box. 

340 """ 

341 c = gf.Component() 

342 flipmon_ref = c << flipmon( 

343 inner_circle_radius=inner_circle_radius, 

344 outer_ring_radius=outer_ring_radius, 

345 outer_ring_width=outer_ring_width, 

346 top_circle_radius=top_circle_radius, 

347 junction_spec=junction_spec, 

348 junction_displacement=junction_displacement, 

349 layer_metal=layer_metal, 

350 layer_metal_top=layer_metal_top, 

351 ) 

352 m1_bbox_radius = outer_ring_radius + outer_ring_width / 2 + m1_etch_extension_gap 

353 m2_bbox_radius = top_circle_radius + m2_etch_extension_gap 

354 

355 for etch_layer, draw_layer, bbox_radius in [ 

356 (LAYER.M1_ETCH, LAYER.M1_DRAW, m1_bbox_radius), 

357 (LAYER.M2_ETCH, LAYER.M2_DRAW, m2_bbox_radius), 

358 ]: 

359 bbox_comp = gf.boolean( 

360 A=gf.components.circle( 

361 radius=bbox_radius, 

362 layer=etch_layer, 

363 ), 

364 B=c, 

365 operation="-", 

366 layer=etch_layer, 

367 layer1=etch_layer, 

368 layer2=draw_layer, 

369 ) 

370 c.absorb(c.add_ref(bbox_comp)) 

371 

372 c.add_ports(flipmon_ref.ports) 

373 return c 

374 

375 

376@gf.cell(check_instances=False) 

377def xmon_transmon( 

378 arm_width: tuple[float, float, float, float] = (30.0, 20.0, 30.0, 20.0), 

379 arm_lengths: tuple[float, float, float, float] = (160.0, 120.0, 160.0, 120.0), 

380 gap_width: float = 10.0, 

381 junction_spec: ComponentSpec = squid_junction, 

382 junction_displacement: DCplxTrans | None = None, 

383 layer_metal: LayerSpec = LAYER.M1_DRAW, 

384 layer_etch: LayerSpec = LAYER.M1_ETCH, 

385) -> Component: 

386 """Creates an Xmon style transmon qubit with cross-shaped geometry. 

387 

388 An Xmon transmon consists of a cross-shaped capacitor pad with four arms 

389 extending from a central region, connected by a Josephson junction at the center. 

390 The design provides better control over the coupling to readout resonators 

391 and neighboring qubits through the individual arm geometries. 

392 

393 See :cite:`barendsCoherentJosephsonQubit2013a` for details about the Xmon design. 

394 

395 Args: 

396 arm_width: Tuple of (top, right, bottom, left) arm widths in μm. 

397 arm_lengths: Tuple of (top, right, bottom, left) arm lengths in μm. 

398 Computed from center to end of each arm. 

399 gap_width: Width of the etched gap around arms in μm. 

400 junction_spec: Component specification for the Josephson junction component. 

401 junction_displacement: Optional complex transformation to apply to the junction. 

402 layer_metal: Layer for the metal pads. 

403 layer_etch: Layer for the etched regions. 

404 

405 Returns: 

406 Component: A gdsfactory component with the Xmon transmon geometry. 

407 """ 

408 c = Component() 

409 

410 arm_width_top, arm_width_right, arm_width_bottom, arm_width_left = arm_width 

411 arm_length_top, arm_length_right, arm_length_bottom, arm_length_left = arm_lengths 

412 

413 # Define arm configurations: (size, move_offset) 

414 arm_configs = [ 

415 ((arm_width_top, arm_length_top), (-arm_width_top / 2, arm_length_top * 0)), 

416 ( 

417 (arm_length_right, arm_width_right), 

418 (arm_length_right * 0, -arm_width_right / 2), 

419 ), 

420 ( 

421 (arm_width_bottom, arm_length_bottom), 

422 (-arm_width_bottom / 2, -arm_length_bottom), 

423 ), 

424 ((arm_length_left, arm_width_left), (-arm_length_left, -arm_width_left / 2)), 

425 ] 

426 

427 # Create the four arms extending from the center 

428 for size, move_offset in arm_configs: 

429 arm = gf.components.rectangle( 

430 size=size, 

431 layer=layer_metal, 

432 ) 

433 arm_ref = c.add_ref(arm) 

434 arm_ref.move(move_offset) 

435 c.absorb(arm_ref) 

436 c.flatten(merge=True) 

437 

438 # Create etch by sizing drawn metal 

439 layer_metal_tuple = gf.get_layer_tuple(layer_metal) 

440 etch_region = gf.component.size( 

441 Region( 

442 kdb.RecursiveShapeIterator( 

443 c.kcl.layout, 

444 c._base.kdb_cell, # pyright: ignore[reportPrivateUsage] 

445 c.kcl.layout.layer(layer_metal_tuple[0], layer_metal_tuple[1]), 

446 ) 

447 ), 

448 gap_width, 

449 ) 

450 etch_component = gf.Component() 

451 etch_component.add_polygon(etch_region, layer=layer_etch) 

452 

453 # Remove additive metal from etch 

454 etch_component = gf.boolean( 

455 A=etch_component, 

456 B=c, 

457 operation="-", 

458 layer=LAYER.M1_ETCH, 

459 layer1=LAYER.M1_ETCH, 

460 layer2=LAYER.M1_DRAW, 

461 ) 

462 etch_ref = c.add_ref(etch_component) 

463 c.absorb(etch_ref) 

464 

465 # Create and place Josephson junction at the y-center of the gap 

466 junction_ref = c.add_ref(gf.get_component(junction_spec)) 

467 junction_ref.rotate(-45) 

468 junction_ref.dcenter = (0, c.ymin + gap_width / 2) 

469 if junction_displacement: 

470 junction_ref.transform(junction_displacement) 

471 

472 # Add ports at the ends of each arm for connectivity 

473 for name, width, center, orientation in zip( 

474 ["top_arm", "right_arm", "bottom_arm", "left_arm"], 

475 arm_width, 

476 [ 

477 (0, arm_length_top), 

478 (arm_length_right, 0), 

479 (0, -arm_length_bottom), 

480 (-arm_length_left, 0), 

481 ], 

482 [90, 0, 270, 180], 

483 ): 

484 c.add_port( 

485 name=name, 

486 center=center, 

487 width=width, 

488 orientation=orientation, 

489 layer=layer_metal, 

490 ) 

491 

492 # Add junction port 

493 c.add_port( 

494 name="junction", 

495 center=junction_ref.dcenter, 

496 width=junction_ref.size_info.height, 

497 orientation=90, 

498 layer=LAYER.JJ_AREA, 

499 port_type="placement", 

500 ) 

501 

502 # Add metadata 

503 c.info["qubit_type"] = "xmon" 

504 

505 return c 

506 

507 

508if __name__ == "__main__": 

509 show_components( 

510 double_pad_transmon, 

511 partial(double_pad_transmon, junction_displacement=DCplxTrans(0, 150)), 

512 double_pad_transmon_with_bbox, 

513 flipmon, 

514 flipmon_with_bbox, 

515 xmon_transmon, 

516 ) 

517 

518 # Visualize flip-chip flipmon 

519 # c = gf.Component() 

520 # (c << flipmon_with_bbox()).move((0, 0)) 

521 # c << rectangle(size=(500, 500), layer=LAYER.SIM_AREA, centered=True) 

522 # to_stl(c, "flipmon.stl", layer_stack=LAYER_STACK_FLIP_CHIP) 

523 # scene = c.to_3d(layer_stack=LAYER_STACK_FLIP_CHIP) 

524 # scene.show()