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

57 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-02 17:50 +0000

1"""Josephson junction components.""" 

2 

3from __future__ import annotations 

4 

5import gdsfactory as gf 

6from gdsfactory.component import Component 

7from gdsfactory.typings import ComponentSpec, CrossSectionSpec, LayerSpec 

8from klayout.db import DCplxTrans 

9 

10from qpdk.cells.waveguides import straight 

11from qpdk.helper import show_components 

12from qpdk.tech import ( 

13 LAYER, 

14 josephson_junction_cross_section_narrow, 

15 josephson_junction_cross_section_wide, 

16) 

17 

18 

19@gf.cell(tags=("junctions",)) 

20def single_josephson_junction_wire( 

21 wide_straight_length: float = 8.3, 

22 narrow_straight_length: float = 0.5, 

23 taper_length: float = 4.7, 

24 cross_section_wide: CrossSectionSpec = josephson_junction_cross_section_wide, 

25 cross_section_narrow: CrossSectionSpec = josephson_junction_cross_section_narrow, 

26 layer_patch: LayerSpec = LAYER.JJ_PATCH, 

27 size_patch: tuple[float, float] = (1.5, 1.0), 

28) -> Component: 

29 r"""Creates a single wire to use in a Josephson junction. 

30 

31 .. svgbob:: 

32 

33 ┌───┐ 

34 │o1 │━━━━────╶╶╶╶╶ o2 

35 └───┘ 

36 wide narrow 

37 

38 Args: 

39 wide_straight_length: Length of the wide straight section in µm. 

40 narrow_straight_length: Length of the narrow straight section in µm. 

41 taper_length: Length of the taper section in µm. 

42 cross_section_wide: Cross-section specification for the wide section. 

43 cross_section_narrow: Cross-section specification for the narrow section. 

44 layer_patch: Layer for the patch that creates the overlap region. 

45 size_patch: Size of the patch that creates the overlap region. 

46 

47 Returns: 

48 The junction wire component. 

49 """ 

50 c = Component() 

51 

52 # Widest straight section with patch 

53 wide_straight_ref = c << straight( 

54 length=wide_straight_length, cross_section=cross_section_wide 

55 ) 

56 

57 # Add the tapered transition section 

58 taper_ref = c << gf.c.taper_cross_section( 

59 length=taper_length, 

60 cross_section1=cross_section_wide, 

61 cross_section2=cross_section_narrow, 

62 linear=True, 

63 ) 

64 

65 # Narrow straight section with overlap 

66 narrow_straight_ref = c << straight( 

67 length=narrow_straight_length, cross_section=cross_section_narrow 

68 ) 

69 

70 # Connect all 

71 taper_ref.connect("o1", wide_straight_ref.ports["o2"]) 

72 narrow_straight_ref.connect("o1", taper_ref.ports["o2"]) 

73 

74 # Add patch to wide section 

75 if layer_patch: 

76 patch = c << gf.components.rectangle( 

77 size=size_patch, 

78 layer=layer_patch, 

79 centered=True, 

80 ) 

81 # Overlap with one fourth offset to one side 

82 patch.move(( 

83 wide_straight_ref.dbbox().p1.x - size_patch[0] / 4, 

84 wide_straight_ref.y, 

85 )) 

86 

87 # Add port at wide end 

88 c.add_port( 

89 port=wide_straight_ref.ports["o1"], name="o1", cross_section=cross_section_wide 

90 ) 

91 # Add port at narrow end 

92 c.add_port( 

93 port=narrow_straight_ref.ports["o2"], 

94 name="o2", 

95 cross_section=cross_section_narrow, 

96 ) 

97 

98 return c 

99 

100 

101@gf.cell(tags=("junctions",)) 

102def josephson_junction( 

103 junction_overlap_displacement: float = 1.8, 

104 wide_straight_length: float = 8.3, 

105 narrow_straight_length: float = 0.5, 

106 taper_length: float = 4.7, 

107 cross_section_wide: CrossSectionSpec = josephson_junction_cross_section_wide, 

108 cross_section_narrow: CrossSectionSpec = josephson_junction_cross_section_narrow, 

109 layer_patch: LayerSpec = LAYER.JJ_PATCH, 

110 size_patch: tuple[float, float] = (1.5, 1.0), 

111) -> Component: 

112 r"""Creates a single Josephson junction component. 

113 

114 A Josephson junction consists of two superconducting electrodes separated 

115 by a thin insulating barrier allowing tunnelling. 

116 

117 .. svgbob:: 

118 

119 right_wide 

120 ┌───┐ ╷ overlap 

121 │ │━━━━────╶╶╷╶╶ 

122 └───┘ ╷ 

123 

124 

125 

126 

127 ┌───┐ 

128 │ │ 

129 └───┘ 

130 left_wide 

131 

132 Args: 

133 junction_overlap_displacement: Displacement of the overlap region in µm. 

134 Measured from the centers of the junction ports. 

135 wide_straight_length: Length of the wide straight section in µm. 

136 narrow_straight_length: Length of the narrow straight section in µm. 

137 taper_length: Length of the taper section in µm. 

138 cross_section_wide: Cross-section specification for the wide section. 

139 cross_section_narrow: Cross-section specification for the narrow section. 

140 layer_patch: Layer for the patch that creates the overlap region. 

141 size_patch: Size of the patch that creates the overlap region. 

142 

143 Returns: 

144 The Josephson junction component. 

145 """ 

146 c = Component() 

147 

148 # Left wire 

149 left_wire = c << single_josephson_junction_wire( 

150 wide_straight_length=wide_straight_length, 

151 narrow_straight_length=narrow_straight_length, 

152 taper_length=taper_length, 

153 cross_section_wide=cross_section_wide, 

154 cross_section_narrow=cross_section_narrow, 

155 layer_patch=layer_patch, 

156 size_patch=size_patch, 

157 ) 

158 

159 # Right wire 

160 right_wire = c << single_josephson_junction_wire( 

161 wide_straight_length=wide_straight_length, 

162 narrow_straight_length=narrow_straight_length, 

163 taper_length=taper_length, 

164 cross_section_wide=cross_section_wide, 

165 cross_section_narrow=cross_section_narrow, 

166 layer_patch=layer_patch, 

167 size_patch=size_patch, 

168 ) 

169 

170 total_length = wide_straight_length + narrow_straight_length + taper_length 

171 # Position left wire on top of right wire with rotation 

172 left_wire.dcplx_trans = ( 

173 right_wire.ports["o2"].dcplx_trans 

174 * DCplxTrans.R90 

175 * DCplxTrans( 

176 -total_length + junction_overlap_displacement, 

177 0, 

178 ) 

179 ) 

180 right_wire.dcplx_trans *= DCplxTrans(junction_overlap_displacement, 0) 

181 

182 # Add ports at wide ends 

183 c.add_port( 

184 name="left_wide", 

185 port=left_wire.ports["o1"], 

186 ) 

187 c.add_port( 

188 name="right_wide", 

189 port=right_wire.ports["o1"], 

190 ) 

191 # One port at overlap 

192 c.add_port( 

193 name="overlap", 

194 center=( 

195 left_wire.ports["o2"].dcplx_trans 

196 * DCplxTrans(-junction_overlap_displacement, 0) 

197 ).disp.to_p(), 

198 width=left_wire.ports["o2"].width, 

199 orientation=left_wire.ports["o2"].orientation, 

200 layer=left_wire.ports["o2"].layer, 

201 port_type="placement", 

202 ) 

203 

204 return c 

205 

206 

207@gf.cell(tags=("junctions",)) 

208def josephson_junction_long(**kwargs) -> Component: 

209 """Josephson junction with wide_straight_length=12. 

210 

211 Returns: 

212 The Josephson junction component with long wires. 

213 """ 

214 return josephson_junction(wide_straight_length=12, **kwargs) 

215 

216 

217@gf.cell(tags=("junctions",)) 

218def squid_junction_long(**kwargs) -> Component: 

219 """SQUID junction with josephson_junction_long. 

220 

221 Returns: 

222 The SQUID junction component with long wires. 

223 """ 

224 return squid_junction(junction_spec=josephson_junction_long, **kwargs) 

225 

226 

227@gf.cell(tags=("junctions",)) 

228def squid_junction( 

229 junction_spec: ComponentSpec = josephson_junction, 

230 loop_area: float = 4, 

231) -> Component: 

232 """Creates a SQUID (Superconducting Quantum Interference Device) junction component. 

233 

234 A SQUID consists of two Josephson junctions connected in parallel, forming a loop. 

235 

236 See :cite:`clarkeSQUIDHandbook2004` for details. 

237 

238 Args: 

239 junction_spec: Component specification for the Josephson junction component. 

240 loop_area: Area of the SQUID loop in µm². 

241 This does not take into account the junction wire widths. 

242 

243 Returns: 

244 The SQUID junction component. 

245 """ 

246 c = Component() 

247 

248 junction_comp = gf.get_component(junction_spec) 

249 

250 left_junction = c << junction_comp 

251 right_junction = c << junction_comp 

252 

253 # Form a cross by positioning overlaps on top of each other 

254 right_junction.dcplx_trans = ( 

255 left_junction.ports["overlap"].dcplx_trans 

256 * DCplxTrans.R90 

257 * DCplxTrans( 

258 -left_junction.xmax 

259 + (left_junction.xmax - left_junction.ports["overlap"].x), 

260 0, 

261 ) 

262 ) 

263 

264 # Start adding area by displacing junctions 

265 displacement_xy = loop_area**0.5 

266 right_junction.dcplx_trans *= DCplxTrans((displacement_xy,) * 2) 

267 

268 # Add ports from junctions with descriptive names 

269 for junction_name, junction in [("left", left_junction), ("right", right_junction)]: 

270 for port_side in ["left", "right"]: 

271 port_name = f"{junction_name}_{port_side}_wide" 

272 c.add_port(name=port_name, port=junction.ports[f"{port_side}_wide"]) 

273 

274 # Overlaps and their center 

275 c.add_port( 

276 name="left_overlap", 

277 port=left_junction.ports["overlap"], 

278 port_type="placement", 

279 ) 

280 c.add_port( 

281 name="right_overlap", 

282 port=right_junction.ports["overlap"], 

283 port_type="placement", 

284 ) 

285 c.add_port( 

286 name="loop_center", 

287 center=( 

288 ( 

289 left_junction.ports["overlap"].dcplx_trans.disp 

290 + right_junction.ports["overlap"].dcplx_trans.disp 

291 ) 

292 / 2 

293 ).to_p(), 

294 layer=left_junction.ports["overlap"].layer, 

295 width=left_junction.ports["overlap"].width, 

296 port_type="placement", 

297 ) 

298 return c 

299 

300 

301if __name__ == "__main__": 

302 show_components(josephson_junction, squid_junction)