Coverage for qpdk / models / resonator.py: 100%

49 statements  

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

1"""Resonators.""" 

2 

3from typing import Any 

4 

5import jax.numpy as jnp 

6import sax 

7from gdsfactory.typings import CrossSectionSpec 

8from sax.models.rf import capacitor, electrical_open, electrical_short, tee 

9 

10from qpdk.helper import deprecated 

11from qpdk.models.constants import DEFAULT_FREQUENCY, c_0 

12from qpdk.models.couplers import cpw_cpw_coupling_capacitance 

13from qpdk.models.cpw import ( 

14 cpw_parameters, 

15 cpw_z0_from_cross_section, 

16 get_cpw_dimensions, 

17) 

18from qpdk.models.waveguides import straight, straight_shorted 

19 

20 

21def quarter_wave_resonator_coupled( 

22 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

23 length: float = 5000.0, 

24 coupling_gap: float = 0.27, 

25 coupling_straight_length: float = 20, 

26 cross_section: CrossSectionSpec = "cpw", 

27) -> sax.SDict: 

28 """Model for a quarter-wave coplanar waveguide resonator coupled to a probeline. 

29 

30 Args: 

31 cross_section: The cross-section of the CPW. 

32 f: Frequency in Hz at which to evaluate the S-parameters. 

33 length: Total length of the resonator in μm. 

34 coupling_gap: Gap between the resonator and the probeline in μm. 

35 coupling_straight_length: Length of the coupling section in μm. 

36 

37 Returns: 

38 sax.SDict: S-parameters dictionary 

39 """ 

40 f_arr = jnp.asarray(f) 

41 

42 instances = { 

43 "resonator": resonator_coupled( 

44 f=f_arr, 

45 length=length, 

46 coupling_gap=coupling_gap, 

47 coupling_straight_length=coupling_straight_length, 

48 cross_section=cross_section, 

49 open_start=True, 

50 open_end=False, 

51 ), 

52 "short": electrical_short(f=f_arr), 

53 } 

54 

55 connections = { 

56 "resonator,resonator_o2": "short,o1", 

57 } 

58 

59 ports = { 

60 "coupling_o1": "resonator,coupling_o1", 

61 "coupling_o2": "resonator,coupling_o2", 

62 "resonator_o1": "resonator,resonator_o1", 

63 } 

64 

65 return sax.evaluate_circuit_fg((connections, ports), instances) 

66 

67 

68def resonator_coupled( 

69 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

70 length: float = 5000.0, 

71 coupling_gap: float = 0.27, 

72 coupling_straight_length: float = 20, 

73 cross_section: CrossSectionSpec = "cpw", 

74 open_start: bool = True, 

75 open_end: bool = False, 

76) -> sax.SDict: 

77 """Model for a coplanar waveguide resonator coupled to a probeline. 

78 

79 Args: 

80 cross_section: The cross-section of the CPW. 

81 f: Frequency in Hz at which to evaluate the S-parameters. 

82 length: Total length of the resonator in μm. 

83 coupling_gap: Gap between the resonator and the probeline in μm. 

84 coupling_straight_length: Length of the coupling section in μm. 

85 open_start: If True, adds an electrical open at the start. 

86 open_end: If True, adds an electrical open at the end. 

87 

88 Returns: 

89 sax.SDict: S-parameters dictionary with 4 ports. 

90 """ 

91 f_arr = jnp.asarray(f) 

92 

93 capacitor_settings = { 

94 "capacitance": cpw_cpw_coupling_capacitance( 

95 f_arr, coupling_straight_length, coupling_gap, cross_section 

96 ), 

97 "z0": cpw_z0_from_cross_section(cross_section, f_arr), 

98 } 

99 

100 instances = { 

101 "coupling_1": straight( 

102 f=f_arr, length=coupling_straight_length / 2, cross_section=cross_section 

103 ), 

104 "coupling_2": straight( 

105 f=f_arr, length=coupling_straight_length / 2, cross_section=cross_section 

106 ), 

107 "resonator_1": straight( 

108 f=f_arr, length=coupling_straight_length / 2, cross_section=cross_section 

109 ), 

110 "resonator_2": straight( 

111 f=f_arr, 

112 length=length - coupling_straight_length / 2, 

113 cross_section=cross_section, 

114 ), 

115 "tee_1": tee(f=f_arr), 

116 "tee_2": tee(f=f_arr), 

117 "capacitor": capacitor(f=f_arr, **capacitor_settings), 

118 } 

119 

120 connections = { 

121 "coupling_1,o2": "tee_1,o1", 

122 "coupling_2,o1": "tee_1,o2", 

123 "resonator_1,o2": "tee_2,o1", 

124 "resonator_2,o1": "tee_2,o2", 

125 "tee_1,o3": "capacitor,o1", 

126 "tee_2,o3": "capacitor,o2", 

127 } 

128 

129 ports = { 

130 "coupling_o1": "coupling_1,o1", 

131 "coupling_o2": "coupling_2,o2", 

132 } 

133 

134 if open_start: 

135 instances["open_start_term"] = electrical_open(f=f_arr, n_ports=2) 

136 connections["resonator_1,o1"] = "open_start_term,o1" 

137 ports["resonator_o1"] = "open_start_term,o2" 

138 else: 

139 ports["resonator_o1"] = "resonator_1,o1" 

140 

141 if open_end: 

142 instances["open_end_term"] = electrical_open(f=f_arr, n_ports=2) 

143 connections["resonator_2,o2"] = "open_end_term,o1" 

144 ports["resonator_o2"] = "open_end_term,o2" 

145 else: 

146 ports["resonator_o2"] = "resonator_2,o2" 

147 

148 return sax.evaluate_circuit_fg((connections, ports), instances) 

149 

150 

151def resonator_frequency( 

152 *, 

153 length: float, 

154 epsilon_eff: float | None = None, 

155 media: Any = None, 

156 cross_section: CrossSectionSpec = "cpw", 

157 is_quarter_wave: bool = True, 

158) -> float: 

159 r"""Calculate the resonance frequency of a quarter- or half-wave CPW resonator. 

160 

161 .. math:: 

162 

163 \begin{aligned} 

164 f &= \frac{v_p}{4L} \mathtt{ (quarter-wave resonator)} \\ 

165 f &= \frac{v_p}{2L} \mathtt{ (half-wave resonator)} 

166 \end{aligned} 

167 

168 The phase velocity is :math:`v_p = c_0 / \sqrt{\varepsilon_{\mathrm{eff}}}`. 

169 

170 See :cite:`simonsCoplanarWaveguideCircuits2001,m.pozarMicrowaveEngineering2012` for details. 

171 

172 Args: 

173 length: Length of the resonator in μm. 

174 epsilon_eff: Effective permittivity. If ``None`` (default), 

175 computed from *cross_section* using :func:`~qpdk.models.cpw.cpw_parameters`. 

176 media: Deprecated. Use *epsilon_eff* or *cross_section* instead. 

177 cross_section: Cross-section specification (used only when 

178 *epsilon_eff* and *media* are not provided). 

179 is_quarter_wave: If True, calculates for a quarter-wave resonator; if False, for a half-wave resonator. 

180 default is True. 

181 

182 Returns: 

183 float: Resonance frequency in Hz. 

184 """ 

185 if epsilon_eff is None: 

186 if media is not None: 

187 deprecated( 

188 "The 'media' argument is deprecated. Use 'epsilon_eff' or 'cross_section' instead." 

189 )(lambda: None)() 

190 epsilon_eff = float(jnp.real(jnp.mean(media.ep_r))) 

191 else: 

192 width, gap = get_cpw_dimensions(cross_section) 

193 epsilon_eff, _z0 = cpw_parameters(width, gap) 

194 

195 v_p = c_0 / jnp.sqrt(epsilon_eff) 

196 coefficient = 4 if is_quarter_wave else 2 

197 return float(jnp.squeeze(v_p / (coefficient * length * 1e-6))) 

198 

199 

200def resonator( 

201 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

202 length: sax.Float = 1000, 

203 cross_section: CrossSectionSpec = "cpw", 

204) -> sax.SType: 

205 """S-parameter model for a simple transmission line resonator. 

206 

207 Args: 

208 f: Array of frequency points in Hz 

209 length: Physical length in µm 

210 cross_section: The cross-section of the waveguide. 

211 

212 Returns: 

213 sax.SType: S-parameters dictionary 

214 """ 

215 return straight(f=f, length=length, cross_section=cross_section) 

216 

217 

218def resonator_half_wave( 

219 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

220 length: sax.Float = 1000, 

221 cross_section: CrossSectionSpec = "cpw", 

222) -> sax.SType: 

223 """S-parameter model for a half-wave resonator (open at both ends). 

224 

225 Args: 

226 f: Array of frequency points in Hz 

227 length: Physical length in µm 

228 cross_section: The cross-section of the waveguide. 

229 

230 Returns: 

231 sax.SType: S-parameters dictionary 

232 """ 

233 return straight(f=f, length=length, cross_section=cross_section) 

234 

235 

236def resonator_quarter_wave( 

237 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

238 length: sax.Float = 1000, 

239 cross_section: CrossSectionSpec = "cpw", 

240) -> sax.SType: 

241 """S-parameter model for a quarter-wave resonator (shorted at one end). 

242 

243 Args: 

244 f: Array of frequency points in Hz 

245 length: Physical length in µm 

246 cross_section: The cross-section of the waveguide. 

247 

248 Returns: 

249 sax.SType: S-parameters dictionary 

250 """ 

251 return straight_shorted(f=f, length=length, cross_section=cross_section)