约瑟夫森$0$结、$\pi$结与$\varphi$结

Image 1

基本概念

Josephson 结通常由两个超导体通过一个弱连接区域连接而成,例如

其中 S 表示超导体,N 表示普通金属,F 表示铁磁体。左右两个超导体的序参量可以写成

Josephson 结的核心变量是左右超导体之间的相位差

体系的自由能一般是相位差的周期函数

Josephson 电流由自由能对相位差的导数给出

因此,Josephson 结的基态相位由自由能最低点决定。不同类型的 Josephson 结,本质区别就在于$F(\phi)$的最低点位于哪里。

$0$结

最普通的 Josephson 结是$0$结。其自由能最低点位于$\phi=0$,简单有效自由能可以写成

其中$E_J>0$,对应的Josephson电流为

此时体系倾向于让两个超导体保持同相位

所以$0$结的物理含义是:左右超导体之间的 Josephson 耦合为正,基态相位差为零。

$\pi$结

$\pi$结的自由能最低点位于$\phi=\pi$,它可以看成 Josephson 耦合常数变号。自由能可以写成

对应电流为

因此,$\pi$结中左右两个超导体在基态下更倾向于相差一个$\pi$相位

$\pi$结常见于含有磁性中间层的 Josephson 结,例如 S/F/S 结。在铁磁层中,交换场会使 Cooper 对获得有限动量,从而导致配对振荡。当结长或交换场满足某些条件时,最低能量状态从$0$相转变为$\pi$相。

$\varphi$结

$\varphi$结是更一般的情况,其自由能最低点既不在$0$,也不在$\pi$,而是在

也就是说,体系自发选择一个非平庸相位差。为了稳定这种相位,通常需要高阶 Josephson 谐波。例如自由能可以写成

对应电流为

平衡态满足$I(\phi)=0$,除了$\varphi=0,\pi$之外,还可能存在非平庸解

当该解存在并且对应自由能极小值时,体系就是$\varphi$结。通常需要

此时基态相位为

所以$\varphi$结的物理含义是:左右超导体既不倾向于同相,也不倾向于反相,而是自发形成一个介于$0$和$$pi之间的相位差。

$0-\pi$转变

在许多 Josephson 结中,改变中间区长度、交换场强度、化学势或温度等参数,会导致基态从$0$相变为$\pi$相。这个过程称为$0-\pi$转变。在有效模型中,可以理解为第一谐波系数 $J_1$变号

当$J_1>0$,基态为$0$结;当$J_1<0$,基态为$\pi$结。在转变点附近$J_1\approx 0$,第一谐波被压低,此时第二谐波$J_2$可能变得重要,从而有机会形成$\varphi$结。

代码模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Effective Josephson-junction model for 0, pi, and phi junctions.

Free energy:
F(phi) = -J1 cos(phi) - J2 cos(2 phi)

Current-phase relation:
I(phi) = dF/dphi = J1 sin(phi) + 2 J2 sin(2 phi)

The code saves:
1. data/*.dat
2. figures/free_energy_three_junctions.png/pdf
3. figures/cpr_three_junctions.png/pdf
"""

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
from pathlib import Path


# =========================================================
# 0. Global switches
# =========================================================
DO_CALCULATE_AND_SAVE = True
DO_PLOT_FROM_DATA = True


# =========================================================
# 1. Output directories
# =========================================================
ROOT_DIR = Path("josephson_cpr_demo")
DATA_DIR = ROOT_DIR / "data"
FIG_DIR = ROOT_DIR / "figures"

DATA_DIR.mkdir(parents=True, exist_ok=True)
FIG_DIR.mkdir(parents=True, exist_ok=True)


# =========================================================
# 2. Plot style
# =========================================================
def set_plot_style():
plt.rcParams.update({
"figure.dpi": 120,
"savefig.dpi": 300,
"font.family": "Times New Roman",
"mathtext.fontset": "stix",
"font.size": 15,
"axes.labelsize": 18,
"axes.linewidth": 1.25,
"xtick.labelsize": 15,
"ytick.labelsize": 15,
"legend.fontsize": 13,
"lines.linewidth": 2.8,
"xtick.direction": "in",
"ytick.direction": "in",
"xtick.top": True,
"ytick.right": True,
"xtick.major.size": 5,
"ytick.major.size": 5,
"xtick.major.width": 1.15,
"ytick.major.width": 1.15,
"figure.facecolor": "white",
"axes.facecolor": "white",
})


# =========================================================
# 3. Model definitions
# =========================================================
def free_energy(phi, J1, J2):
"""
F(phi) = -J1 cos(phi) - J2 cos(2 phi)
"""
return -J1 * np.cos(phi) - J2 * np.cos(2.0 * phi)


def josephson_current(phi, J1, J2):
"""
I(phi) = dF/dphi = J1 sin(phi) + 2 J2 sin(2 phi)
"""
return J1 * np.sin(phi) + 2.0 * J2 * np.sin(2.0 * phi)


def find_ground_state_phase(phi, F):
"""
Return all numerically degenerate minima within a tolerance.
"""
F_min = np.min(F)
tol = 1e-4
idx = np.where(np.abs(F - F_min) < tol)[0]

# Collect separated minima only
minima = []
for i in idx:
if not minima:
minima.append(phi[i])
elif abs(phi[i] - minima[-1]) > 0.02:
minima.append(phi[i])

return np.array(minima), F_min


# =========================================================
# 4. Parameters
# =========================================================
N_PHI = 2001
PHI = np.linspace(-np.pi, np.pi, N_PHI)

# Parameter sets:
# 0 junction: minimum at phi = 0
# pi junction: minimum at phi = pi
# phi junction: minima at phi = ±phi0
#
# For the phi junction chosen below:
# cos(phi0) = -J1 / (4 J2) = 1/2
# phi0 = pi/3
CASES = {
"0_junction": {
"label": r"$0$ junction",
"J1": 1.00,
"J2": 0.10,
"color": "#0072B2",
"linestyle": "-",
"marker": "o",
},
"pi_junction": {
"label": r"$\pi$ junction",
"J1": -1.00,
"J2": 0.10,
"color": "#D55E00",
"linestyle": "--",
"marker": "s",
},
"phi_junction": {
"label": r"$\varphi$ junction",
"J1": 0.40,
"J2": -0.20,
"color": "#CC79A7",
"linestyle": "-.",
"marker": "^",
},
}


# =========================================================
# 5. Calculation and save data
# =========================================================
def calculate_and_save():
summary_lines = []
summary_lines.append("# case J1 J2 phi_min/pi")

for key, cfg in CASES.items():
J1 = cfg["J1"]
J2 = cfg["J2"]

F = free_energy(PHI, J1, J2)
I = josephson_current(PHI, J1, J2)
F_shift = F - np.min(F)

minima, F_min = find_ground_state_phase(PHI, F)

data = np.column_stack([
PHI,
PHI / np.pi,
F,
F_shift,
I,
])

header = (
"phi phi_over_pi F_phi F_phi_minus_Fmin I_phi\n"
f"J1 = {J1:.12f}, J2 = {J2:.12f}"
)

np.savetxt(DATA_DIR / f"{key}.dat", data, header=header)

for phimin in minima:
summary_lines.append(
f"{key:16s} {J1:+.8f} {J2:+.8f} {phimin / np.pi:+.8f}"
)

with open(DATA_DIR / "summary.txt", "w", encoding="utf-8") as f:
f.write("\n".join(summary_lines))

print("Calculation finished.")
print(f"Data saved to: {DATA_DIR}")


# =========================================================
# 6. Helper functions for plotting
# =========================================================
def set_three_ticks(ax, x=True, y=True):
"""
Limit the number of major ticks to at most three.
"""
if x:
ax.set_xticks([-1.0, 0.0, 1.0])
ax.set_xticklabels([r"$-\pi$", r"$0$", r"$\pi$"])

if y:
ax.yaxis.set_major_locator(MaxNLocator(nbins=3))


def load_case_data(key):
path = DATA_DIR / f"{key}.dat"
if not path.exists():
raise FileNotFoundError(f"Missing data file: {path}")
return np.loadtxt(path)


def mark_minima_on_energy(ax, phi_over_pi, F_shift, cfg):
"""
Mark the free-energy minima.
"""
min_val = np.min(F_shift)
tol = 1e-4
idx = np.where(np.abs(F_shift - min_val) < tol)[0]

# Reduce nearby points to separated markers
selected = []
for i in idx:
if not selected:
selected.append(i)
elif abs(phi_over_pi[i] - phi_over_pi[selected[-1]]) > 0.03:
selected.append(i)

ax.scatter(
phi_over_pi[selected],
F_shift[selected],
s=45,
marker=cfg["marker"],
color=cfg["color"],
edgecolor="black",
linewidth=0.6,
zorder=5,
)


# =========================================================
# 7. Plot free energy
# =========================================================
def plot_free_energy():
fig, ax = plt.subplots(figsize=(5.6, 4.2))

for key, cfg in CASES.items():
data = load_case_data(key)

phi_over_pi = data[:, 1]
F_shift = data[:, 3]

ax.plot(
phi_over_pi,
F_shift,
color=cfg["color"],
linestyle=cfg["linestyle"],
linewidth=3.0,
label=cfg["label"],
)

mark_minima_on_energy(ax, phi_over_pi, F_shift, cfg)

ax.set_xlabel(r"$\phi$")
ax.set_ylabel(r"$F(\phi)-F_{\min}$")

ax.set_xlim(-1.0, 1.0)
ax.set_ylim(bottom=-0.03)

set_three_ticks(ax, x=True, y=True)

ax.legend(frameon=True, fancybox=False, edgecolor="black", loc="best")

# ax.text(
# 0.03,
# 0.93,
# r"$F(\phi)=-J_1\cos\phi-J_2\cos2\phi$",
# transform=ax.transAxes,
# ha="left",
# va="top",
# fontsize=14,
# )

fig.tight_layout()
fig.savefig(FIG_DIR / "free_energy_three_junctions.png", bbox_inches="tight")
fig.savefig(FIG_DIR / "free_energy_three_junctions.pdf", bbox_inches="tight")
plt.show()


# =========================================================
# 8. Plot current-phase relation
# =========================================================
def plot_cpr():
fig, ax = plt.subplots(figsize=(5.6, 4.2))

for key, cfg in CASES.items():
data = load_case_data(key)

phi_over_pi = data[:, 1]
I = data[:, 4]

ax.plot(
phi_over_pi,
I,
color=cfg["color"],
linestyle=cfg["linestyle"],
linewidth=3.0,
label=cfg["label"],
)

ax.axhline(0.0, color="black", linewidth=1.0, linestyle=":")

ax.set_xlabel(r"$\phi$")
ax.set_ylabel(r"$I(\phi)$")

ax.set_xlim(-1.0, 1.0)

set_three_ticks(ax, x=True, y=True)

ax.legend(frameon=True, fancybox=False, edgecolor="black", loc="best")

# ax.text(
# 0.03,
# 0.93,
# r"$I(\phi)=J_1\sin\phi+2J_2\sin2\phi$",
# transform=ax.transAxes,
# ha="left",
# va="top",
# fontsize=14,
# )

fig.tight_layout()
fig.savefig(FIG_DIR / "cpr_three_junctions.png", bbox_inches="tight")
fig.savefig(FIG_DIR / "cpr_three_junctions.pdf", bbox_inches="tight")
plt.show()


# =========================================================
# 9. Print physical interpretation
# =========================================================
def print_summary():
print("\nGround-state phase summary")
print("=" * 72)

for key, cfg in CASES.items():
data = load_case_data(key)

phi = data[:, 0]
phi_over_pi = data[:, 1]
F = data[:, 2]

minima, _ = find_ground_state_phase(phi, F)

J1 = cfg["J1"]
J2 = cfg["J2"]

print(f"{cfg['label']:18s}: J1 = {J1:+.3f}, J2 = {J2:+.3f}")

if key == "0_junction":
print(" Expected ground state: phi = 0")
elif key == "pi_junction":
print(" Expected ground state: phi = pi")
elif key == "phi_junction":
if J2 < 0 and abs(J1) < 4.0 * abs(J2):
phi0 = np.arccos(-J1 / (4.0 * J2))
print(f" Expected ground states: phi = ±phi0, phi0/pi = {phi0 / np.pi:.4f}")

print(" Numerical minima:")
for phimin in minima:
print(f" phi/pi = {phimin / np.pi:+.4f}")

print("=" * 72)
print(f"Figures saved to: {FIG_DIR}")


# =========================================================
# 10. Main
# =========================================================
def main():
set_plot_style()

if DO_CALCULATE_AND_SAVE:
calculate_and_save()

if DO_PLOT_FROM_DATA:
plot_free_energy()
plot_cpr()
print_summary()


if __name__ == "__main__":
main()
Image 1 Image 2

参考文献

  1. Disorder-Induced Phase Transitions in Altermagnetic Josephson Junctions

鉴于该网站分享的大都是学习笔记,作者水平有限,若发现有问题可以发邮件给我

  • yxliphy@gmail.com

也非常欢迎喜欢分享的小伙伴投稿