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

57 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-14 10:27 +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 

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 c = Component() 

48 

49 # Widest straight section with patch 

50 wide_straight_ref = c << straight( 

51 length=wide_straight_length, cross_section=cross_section_wide 

52 ) 

53 

54 # Add the tapered transition section 

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

56 length=taper_length, 

57 cross_section1=cross_section_wide, 

58 cross_section2=cross_section_narrow, 

59 linear=True, 

60 ) 

61 

62 # Narrow straight section with overlap 

63 narrow_straight_ref = c << straight( 

64 length=narrow_straight_length, cross_section=cross_section_narrow 

65 ) 

66 

67 # Connect all 

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

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

70 

71 # Add patch to wide section 

72 if layer_patch: 

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

74 size=size_patch, 

75 layer=layer_patch, 

76 centered=True, 

77 ) 

78 # Overlap with one fourth offset to one side 

79 patch.move( 

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

81 ) 

82 

83 # Add port at wide end 

84 c.add_port( 

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

86 ) 

87 # Add port at narrow end 

88 c.add_port( 

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

90 name="o2", 

91 cross_section=cross_section_narrow, 

92 ) 

93 

94 return c 

95 

96 

97@gf.cell 

98def josephson_junction( 

99 junction_overlap_displacement: float = 1.8, 

100 wide_straight_length: float = 8.3, 

101 narrow_straight_length: float = 0.5, 

102 taper_length: float = 4.7, 

103 cross_section_wide: CrossSectionSpec = josephson_junction_cross_section_wide, 

104 cross_section_narrow: CrossSectionSpec = josephson_junction_cross_section_narrow, 

105 layer_patch: LayerSpec = LAYER.JJ_PATCH, 

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

107) -> Component: 

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

109 

110 A Josephson junction consists of two superconducting electrodes separated 

111 by a thin insulating barrier allowing tunnelling. 

112 

113 .. svgbob:: 

114 

115 right_wide 

116 ┌───┐ ╷ overlap 

117 │ │━━━━────╶╶╷╶╶ 

118 └───┘ ╷ 

119 

120 

121 

122 

123 ┌───┐ 

124 │ │ 

125 └───┘ 

126 left_wide 

127 

128 Args: 

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

130 Measured from the centers of the junction ports. 

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

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

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

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

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

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

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

138 """ 

139 c = Component() 

140 

141 # Left wire 

142 left_wire = c << single_josephson_junction_wire( 

143 wide_straight_length=wide_straight_length, 

144 narrow_straight_length=narrow_straight_length, 

145 taper_length=taper_length, 

146 cross_section_wide=cross_section_wide, 

147 cross_section_narrow=cross_section_narrow, 

148 layer_patch=layer_patch, 

149 size_patch=size_patch, 

150 ) 

151 

152 # Right wire 

153 right_wire = c << single_josephson_junction_wire( 

154 wide_straight_length=wide_straight_length, 

155 narrow_straight_length=narrow_straight_length, 

156 taper_length=taper_length, 

157 cross_section_wide=cross_section_wide, 

158 cross_section_narrow=cross_section_narrow, 

159 layer_patch=layer_patch, 

160 size_patch=size_patch, 

161 ) 

162 

163 total_length = wide_straight_length + narrow_straight_length + taper_length 

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

165 left_wire.dcplx_trans = ( 

166 right_wire.ports["o2"].dcplx_trans 

167 * DCplxTrans.R90 

168 * DCplxTrans( 

169 -total_length + junction_overlap_displacement, 

170 0, 

171 ) 

172 ) 

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

174 

175 # Add ports at wide ends 

176 c.add_port( 

177 name="left_wide", 

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

179 ) 

180 c.add_port( 

181 name="right_wide", 

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

183 ) 

184 # One port at overlap 

185 c.add_port( 

186 name="overlap", 

187 center=( 

188 left_wire.ports["o2"].dcplx_trans 

189 * DCplxTrans(-junction_overlap_displacement, 0) 

190 ).disp.to_p(), 

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

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

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

194 port_type="placement", 

195 ) 

196 

197 return c 

198 

199 

200@gf.cell 

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

202 """Josephson junction with wide_straight_length=12.""" 

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

204 

205 

206@gf.cell 

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

208 """SQUID junction with josephson_junction_long.""" 

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

210 

211 

212@gf.cell 

213def squid_junction( 

214 junction_spec: ComponentSpec = josephson_junction, 

215 loop_area: float = 4, 

216) -> Component: 

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

218 

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

220 

221 See :cite:`clarkeSQUIDHandbook2004` for details. 

222 

223 Args: 

224 junction_spec: Component specification for the Josephson junction component. 

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

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

227 """ 

228 c = Component() 

229 

230 junction_comp = gf.get_component(junction_spec) 

231 

232 left_junction = c << junction_comp 

233 right_junction = c << junction_comp 

234 

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

236 right_junction.dcplx_trans = ( 

237 left_junction.ports["overlap"].dcplx_trans 

238 * DCplxTrans.R90 

239 * DCplxTrans( 

240 -left_junction.xmax 

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

242 0, 

243 ) 

244 ) 

245 

246 # Start adding area by displacing junctions 

247 displacement_xy = loop_area**0.5 

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

249 

250 # Add ports from junctions with descriptive names 

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

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

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

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

255 

256 # Overlaps and their center 

257 c.add_port( 

258 name="left_overlap", 

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

260 port_type="placement", 

261 ) 

262 c.add_port( 

263 name="right_overlap", 

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

265 port_type="placement", 

266 ) 

267 c.add_port( 

268 name="loop_center", 

269 center=( 

270 ( 

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

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

273 ) 

274 / 2 

275 ).to_p(), 

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

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

278 port_type="placement", 

279 ) 

280 return c 

281 

282 

283if __name__ == "__main__": 

284 show_components(josephson_junction, squid_junction)