Python-реализация алгоритмов и инструментарий для моделирования динамики сверхпроводникого квантового интерферометра с двумя джозефсоновскими переходами (СКВИД постоянного тока)#

1. Описание модели#

Эффект Джозефсона и джозефсоновский переход:

Связь двух сверхпроводящих слоев посредством тонкого слоя несверхпроводящего барьера образует структуру называемую джозефсоновским переходом (в честь Английского ученого Брайана Джозефсона). При пропускании электрического тока через джозефсоновский переход в зависимости от величины тока наблюдается стационарный и нестационарный эффект Джозефсона.

Стационарный эффект Джозефсона. При пропускании тока ниже критического значения (\(I<I_{c}\)) в джозефсоновском переходе (ДП) отсутсвует напряжение и через переход течет сверхпроводящий ток. Данный ток пропорционален синусу разности фаз параметров порядка сверхпроводящих слоев образующих ДП.

\[ \begin{eqnarray} I_{s}=I_{c}\sin\varphi. \label{eq1} \tag{1} \end{eqnarray} \]

Нестационарный эффект Джозефсона. При увеличении тока выше критического значения (\(I>I_{c}\)) возникает переменное напряжение в ДП и оно пропорционально производной разности фаз

\[ \begin{eqnarray} V=\frac{\hbar}{2e}\frac{d\varphi}{dt}. \label{eq2} \tag{2} \end{eqnarray} \]

Сверхпроводниковые квантовые интерференционные устройства (СКВИД)

Системы содержащие джозефсоновские переходы давно нашли свое применение в различных отраслях науки и техники. Одним из таких примеров является так называемые сверхпроводниковые квантовые интерференционные устройства. В литературе эти устройства известны как СКВИД (от английского SQUID - Superconducting Quantum Interference Device). Различают двух типов СКВИДа: СКВИД постоянного тока (DC-SQUID) и СКВИД переменного тока (RF-SQUID). СКВИД постоянного тока представляет собой сверхпроводниковое кольцо, состоящее из двух параллельно соединенных джозефсоновских переходов, и широко применяется как устройство для измерения очень слабых магнитных полей. А СКВИД переменного тока применяются как чувствительный прибор слабого переменного поля.

Рассмотрим, СКВИД постоянного тока (DC-SQUID), схематический вид которого представлен на Рис.1. Плоскость на которой лежат джозефсоновские переходы и ограничено соединяющими сверхпроводящими проводниками будем называть поверхностью СКВИДа. Предположим, что перпендикулярно к поверхности СКВИДа приложено постоянное магнитное поле \(B\) направленное снизу вверх и оно приведет к потоку \(\Phi_{ext}=BS\), где \(S\) площадь поверхности СКВИДа.

Рис.1 Схематический вид СКВИДа

Рис.2 Эквивалентная схема СКВИДа.

Динамика перехода описывается Resitively Capasitevily Shunted Junction (RCSJ) моделью\cite{r}. В рамках этой модели ДП моделируется как параллельное соединение конденсатора, резистора и сверхпроводника, через которые протекают, соответственно, ток смещения, квазичастичный и сверхпроводящий токи. Учитывая индуктивности сверхпроводящих проводников соединяющие джозефсоновские переходы 1 и 2 Можно написать эквивалентную схему СКВИДа, которая представлена на Рис. 2. Индексами 1 и 2 обозначены, соответсвенно левый и правый и правый ДП. Через СКВИД пропускается внешный ток \(I\), которая распределясь на \(I_{1}\) и \(I_{2}\) течет через джозефсоновские переходы 1 и 2 и в резистивном состоянии ДП-ов приводит к среднему напряжению \(V\). Кроме того из за наличия потока внешнего магнитного поля \(\Phi_{ext}\) возникает циркулирующий сверхпроводящий ток \(J\). Здесь \(L_{1}\) и \(L_{2}\) обозначают индуктивность сверхпроводящих проводников соединяющих джозефсоновские переходы и \(C_{1,2}\), \( R_{1,2}\), \( I_{c,1,2}\) емкость, сопротивление и критический ток ДП, соответвенно. Согласно RCSJ-модели токи \(I_{1}\) и \(I_{2}\) определяются выражениями

\[\begin{split} \label{currents} \left\{\begin{array}{l} \displaystyle I_{1}=C\frac{dV_{1}}{dt}+\frac{V_{1}}{R_{1}} +I_{c1}\sin\varphi_{1}, \\ \displaystyle I_{2}=C\frac{dV_{2}}{dt}+\frac{V_{2}}{R_{2}} +I_{c2}\sin\varphi_{2}, \end{array}\right. \end{split}\]

где \(V_{i}=\frac{\Phi_{0}}{2\pi}\frac{d\varphi_{i}}{dt}\) напряжение в \(i\)-ом переходе, \(\Phi_{0}=\frac{\hbar}{2e}\)-квант магнитного потока. С другой стороны, согласно правила Кирхгофа для токов можно записать

\[\begin{split} \label{currents1} \left\{\begin{array}{l} \displaystyle I_{1}=\frac{I}{2}+J, \\ \displaystyle I_{2}=\frac{I}{2}-J. \end{array}\right. \end{split}\]

Далее для простоты предполагаем, что индуктивности проводников равны друг другу и ДП СКВИДа одинаковы, т.е. полагаем

\[C_{1}=C_{2}=C,\hspace{0.5cm} R_{1}=R_{2}=R,\hspace{0.5cm} L_{1}=L_{2}=L,\hspace{0.5cm} I_{c1}=I_{c2}=I_{c}.\]

Магнитный поток через СКВИД складывается из потока создаваемого внешним полем \(\Phi_{ext}\) и циркулирующим током \(J\) и записывается в виде

\[ \label{flux1} \Phi=\Phi_{ext}+L(I_{1}-I_{2})=\Phi_{ext}+2LJ. \]

Хорошо известно, что магнитный поток через сверхпроводниковые контуры квантуется, т.е. принимает дискретное значения кратное к \(\Phi_{0}\). Выражение для квантования магнитного потока для рассматриваемой системы можно записать

\[ \label{flux2} \varphi_{2}-\varphi_{1}+2\pi n=\frac{2\pi}{\Phi_{0}}\Phi=\frac{2\pi}{\Phi_{0}}(\Phi_{ext}+2LJ), \]

или

\[ \label{flux3} \varphi_{2}-\varphi_{1}+2\pi n=2\pi\varphi_{ext}+\frac{4\pi L}{\Phi_{0}}J, \]

где \(\varphi_{ext}\) безразмерный поток внешнего магнитного поля нормированный на \(\Phi_{0}\). Отсюда можно получить выражению для циркулирующего тока

\[ \label{circulating_current} J=\frac{\Phi_{0}}{4\pi L}[\varphi_{2}-\varphi_{1}+2\pi(n-\varphi_{ext})]. \]

Тогда с учетом (2)-(5) можно получить замкнутую систему уравнений для описания СКВИДа:

\[\begin{split} \label{currents2} \left\{\begin{array}{l} \displaystyle C\frac{dV_{1}}{dt}+\frac{V_{1}}{R}+I_{c}\sin\varphi_{1}=\frac{I}{2}+\frac{\Phi_{0}}{4\pi L}[\varphi_{2}-\varphi_{1}+2\pi(n-\varphi_{ext})],\\ \displaystyle \frac{\Phi_{0}}{2\pi}\frac{d\varphi_{1}}{dt}=V_{1}\\ \displaystyle C\frac{dV_{2}}{dt}+\frac{V_{2}}{R}+I_{c}\sin\varphi_{2}=\frac{I}{2}-\frac{\Phi_{0}}{4\pi L}[\varphi_{2}-\varphi_{1}+2\pi(n-\varphi_{ext})], \\ \displaystyle \frac{\Phi_{0}}{2\pi}\frac{d\varphi_{2}}{dt}=V_{2} \end{array},\right. \end{split}\]

Переходя к безразмерным величинам можно написать

\[\begin{split} \label{eqsystem} \left\{\begin{array}{l} \displaystyle \frac{dV_{1}}{dt}=\frac{1}{\beta_{c}}\bigg\{\frac{I}{2}-\frac{d\varphi_{1}}{dt}-\sin(\varphi_{1})+\frac{1}{2\beta_{L}}[\varphi_{2}-\varphi_{1}+2\pi(n-\varphi_{ext})]\bigg\}\\ \displaystyle \frac{d\varphi_{1}}{dt}=V_{1}\\ \displaystyle \frac{dV_{2}}{dt}=\frac{1}{\beta_{c}}\bigg\{\frac{I}{2}-\frac{d\varphi_{2}}{dt}-\sin(\varphi_{2})-\frac{1}{2\beta_{L}}[\varphi_{2}-\varphi_{1}+2\pi(n-\varphi_{ext})]\bigg\}\\ \displaystyle \frac{d\varphi_{2}}{dt}=V_{2} \end{array},\right. \end{split}\]

где \(\beta_{c}=C\Phi_{0}\omega_{c}^{2}/(2\pi I_{c})\) - параметр МакКамбера, \(\omega_{c}=2\pi R I_{c}/\Phi_{0}\)- характеристическая частота Джозефсона, \(\beta_{L}=2\pi L I_{c}/\Phi_{0}\)- обезразмеренная индуктивность, \(\Phi_{0}=h/(2e)\) - квант магнитного потока, \(\varphi_{ext}=\Phi_{ext}/\Phi_{0}\) - безразмерный поток внешнего магнитного поля. Здесь напряжение нормировано на \(V_{c}=I_{c}R\), ток на \(I_{c}\) и время \(t\) на \(\omega^{-1}_{c}\), где \(\omega_{c}=2\pi V_{c}/\Phi_{0}\). Начальные условия для системы уравнений \eqref{eqsystem} заданы в виде \(V_1(0)=V_2(0)=0\) и \(\varphi_1(0)=\varphi_2(0)=0\).

Постановка задачи: Необходимо вычислить:

  1. Вольт-амперную характеристику (ВАХ) СКВИДа

  2. Зависимость критического тока от потока внешнего магнитного поля.

Алгоритм рассчета

Ниже опишем алгоритм вычисления ВАХ и зависимости критического тока от внешнего магнитного поля. При заданном значений параметров и начальных условий при фиксированном значении тока \(I\) численно решается СДУ (\ref{eqsystem}) в интервале времени [0, Tmax] с шагом \(\Delta t\). Полученная временная зависимость напряжения усредняется по времени по формуле

\[\bar{V}=\frac{1}{T_{\max}-T_{\min}} \int_{T_{\min}}^{T_{\max}}V(t)dt\]

и в результате вычисляется одна точка ВАХ для заданного значения \(I\). Отметим, что при усреднении интегрирования начинается от времени \(Tmin\), после которого решение стабилизируется. Далее меняется значение тока на δI и повторяется вышеописанные действия. При этом вычисленные φ(Tmax) и V (Tmax) для предыдущего значения тока будут использоваться в качестве начального условия для текущего значения тока. Таким образом увеличивая ток до Imax и уменьшая его обратно до нуля получаем ВАХ ДП.

Для определения критического тока в процессе вычисления ВАХ фиксируется значения внешнего тока при которой выполняется условие V>0, т е значение тока при которой возникает напряжение, считается критическим током.

Подключаем библиотеки#

import numpy as np
import matplotlib.pyplot as plt
import math as m
from scipy.integrate import solve_ivp
from functools import partial
from scipy.integrate import odeint
import time

from numbalsoda import lsoda_sig, lsoda, dop853
from numba import njit, cfunc

import seaborn as sns
sns.set()
sns.set(style="whitegrid")

%matplotlib inline

1. Численное решение задачи Коши для системы уравнений (1, 2)#

Функция для вычисления значений правых частей уравнений (1, 2) (Numba version)#

@cfunc(lsoda_sig)
def rhs(t, S, dS, p):
    ''' Функция определяющая правую часть систему уравнений.
        Возвращает значения правой части уравнений при заданных значениях
        параметров.
        S - массив математических функций
        dS - массив производных от математических функций по времени
        p - массив параметров
        '''
    # Задаем параметров модели
    beta_c = p[0]
    beta_L = p[1]
    Pi = p[2]
    n = p[3]
    Icurr = p[4]
    ph_ext = p[5]
    # S массив которая содержит значения функций в начале (начальное условие)
    ph1 = S[0]
    V1 = S[1]
    ph2 = S[2]
    V2 = S[3]
    # Задаем правую часть системы уравнений
    dph1 = V1
    dV1 = (1./beta_c) * ((Icurr/2.) - V1 - np.sin(ph1)
                         + (1./(2.*beta_L)) * (ph2-ph1+2*Pi*(n-ph_ext)))
    dph2 = V2
    dV2 = (1./beta_c) * ((Icurr/2.) - V2 - np.sin(ph2)
                         - (1./(2.*beta_L)) * (ph2-ph1+2*Pi*(n-ph_ext)))
    # Передаем правые части уравнений в массив dS
    dS[0] = dph1
    dS[1] = dV1
    dS[2] = dph2
    dS[3] = dV2
funcptr = rhs.address  # address to ODE function

Задаем значения параметров модели для численного решения системы уравнений#

Важно: необходимо соблюдать типы переменных, когда задаем начальные условия и параметры

beta_c = 0.5  # Параметр диссипации
beta_L = 1.0  # Амплитуда внешнего излучения
Pi = 3.14159265358979323
n = 0  # Период внешнего излучения
Icurr = 2
ph_ext = 0.5
Tmax = 800  # Максимальное значение времени
deltat = 0.05
t0 = 0
nt = int(Tmax / deltat)

Создаем массив с значениями времени для решения уравнения и задаем начальные условия

time_array = np.linspace(0, Tmax, num=nt)
s0 = np.array([0, 0, 0, 0])  # Initial conditions

Создаем массив с значениями параметров

# beta_c, beta_L, Pi, n, Icurr, ph_ext
data = np.array([beta_c, beta_L, Pi, n, Icurr, ph_ext])
data
array([0.5       , 1.        , 3.14159265, 0.        , 2.        ,
       0.5       ])

Численно решаем систему уравнений методом LSODA

%%time
# integrate with lsoda method
usol, success = lsoda(funcptr, s0, time_array, rtol = 1e-8, atol = 1e-8, data = data)
CPU times: user 267 ms, sys: 1.56 ms, total: 268 ms
Wall time: 268 ms

Извлекаем из массива решений значения напряжения V1 и V2

V1time = usol[:, 1]
V2time = usol[:, 3]

Временная зависимость V1 и V2

fig = plt.figure(figsize=(12, 10))
plt.plot(time_array, V1time, label='Временная зависимость', linewidth=2)
plt.xlabel('time', size=12)
plt.ylabel('V1', size=12)
plt.legend(loc='upper right')
plt.show()
_images/2ac7d89f302ef3ff8bdf0a5419163afc654535fcf33ab9452e63da5b551ce277.png
fig = plt.figure(figsize=(12, 10))
plt.plot(time_array, V2time, label='Временная зависимость', linewidth=2)
plt.xlabel('time', size=12)
plt.ylabel('V2', size=12)
plt.legend(loc='upper right')
plt.show()
_images/7ed0df2f77a19c84d8af54effc06cc972574233a81ec203c791b5368e577bd42.png

2. Алгоритм для нахождения вольт-амперной характеристики (ВАХ) СКВИДа#

Для нахождения ВАХ нам нужно полученную временную зависимость усреднить. Для этого необходимо усреднить по отдельности V1 и V2 и затем вычислить среднее от полученных < V1 > и < V2 >. Этот шаг можно реализовать в виде функции которая принимая временные зависимости V1 и V2 возвращает среднее значения < V >

Функция для усреднения напряжения

@njit
def averageV_new(ntmin, nt, deltat, V1, V2):
    ''' Функция для вычисления среднего значения напряжения.
        Возвращает среднее значение напряжения для заданной
            временной зависимости напряжений в первом и втором ДП
        ntmin-номер шага по времени соответсвующая
                нижней границе интегрирования,
        nt - количество точек по времени, в которых найдены решения,
        V1 - временная зависимость напряжения первого ДП,
        V2 - временная зависимость напряжения второго ДП,
        '''
    V1av = np.sum(V1[ntmin: nt])/((nt-ntmin))
    V2av = np.sum(V2[ntmin: nt])/((nt-ntmin))
    Vav = (V1av + V2av)/2
    return Vav

Функция для вычисления одной точки ВАХ

Создаем функцию, которая вычисляет одну точку ВАХ, т.е. выполняет следующие шаги:

  • принимает начальные условия и значения параметров модели

  • решает численно уравнения

  • используя ранее созданную функцию averageV_new усредняет напряжение

  • возвращает среднее напряжение и начальные условия для вычисления следующей точки ВАХ

@njit
def cvcpoint_new(s0, beta_c, beta_L, Pi, n,
                 Icurr, ph_ext, nt, ntmin, t0, deltat):
    ''' Функция для вычисления одной точки ВАХ.
        Возвращает для заданного значения тока вычисленную среднее значение
            напряжения при заданных значений параметров.
        s0 - массив начальных условий,
        beta_c, beta_L, Pi, n, Icurr, ph_ext,-параметры модели
        nt - количество точек по времени, в которых найдены решения,
        ntmin - номер шага по времени соответсвующее
                нижней границе интегрирования,
        t0 - начальное значение времени,
        deltat - шаг по времени
        '''
    data = np.array([beta_c, beta_L, Pi, n, Icurr, ph_ext])
    t_e = np.linspace(t0, nt*deltat, nt)
    # integrate with lsoda method
    usol, success = lsoda(funcptr, s0, t_e, rtol=1e-8, atol=1e-8, data=data)
    # получение результатов из массива usol
    ph1time = usol[:, 0]
    V1time = usol[:, 1]
    ph2time = usol[:, 2]
    V2time = usol[:, 3]
    s0 = np.array([ph1time[nt-1], V1time[nt-1], ph2time[nt-1], V2time[nt-1]])
    Vav = averageV_new(ntmin, nt, deltat, V1time, V2time)
    return np.array([Vav, ph1time[nt-1], V1time[nt-1],
                     ph2time[nt-1], V2time[nt-1]])

Функция для вычисления ВАХ

@njit
def comput_vax(s0, beta_c, beta_L, Pi, n,
               Icurr, ph_ext, nt, ntmin, t0, deltat, a, deltaI):
    ''' Функция для вычисления однопетлевой ВАХ.
        Возвращает для заданного значения параметров массива
            с значениями тока и вычисленного напряжения
        s0 - массив начальных условий,
        beta_c, beta_L, Pi, n, Icurr, ph_ext,-параметры модели
        nt - количество точек по времени, в которых найдены решения,
        ntmin - номер шага по времени соответсвующее
                нижней границе интегрирования,
        t0 - начальное значение времени,
        deltat - шаг по времени
        deltaI - шаг по току
        '''
    Vplot = []
    Iplot = []
    while Icurr < 100:
        res = cvcpoint_new(s0, beta_c, beta_L, Pi, n,
                           Icurr, ph_ext, nt, ntmin, t0, deltat)
        Vav = res[0]
        s0 = res[1:]
        Vplot.append(Vav)
        Iplot.append(Icurr)
        Icurr += a*deltaI
        if(Icurr > Imax):
            a = -1
        if ((Icurr < 0) and (a == -1)):
            break
    return np.column_stack((np.array(Iplot), np.array(Vplot)))

Задаем значения параметров для вычисления ВАХ

Tmin = 50.0  # Начало интервала для интегрирования для усреднения
ntmin = int(Tmin/deltat)
deltaI = 0.001
Icurr = 0.0
a = 1
Imax = 2.2
s0 = np.array([0.0, 0.0, 0.0, 0.0])
res = cvcpoint_new(s0, beta_c, beta_L, Pi, n,
                   Icurr, ph_ext, nt, ntmin, t0, deltat)
print(res)
print(res[0])
print(res[1:])

Вычислим ВАХ

start = time.time()
res = comput_vax(s0, beta_c, beta_L, Pi, n,
                 Icurr, ph_ext, nt, ntmin, t0, deltat, a, deltaI)
end = time.time()

print(end-start)

График ВАХ

fig = plt.figure(figsize=(12, 10))
plt.plot(res[:, 0], res[:, 1], label='ВАХ', linewidth=3.0)
plt.xlabel('I', size=12)
plt.ylabel('V', size=12)
plt.legend(loc='upper left')
plt.show()
_images/798e0ecec1312298ec392eb33aa0c34b2a3a5b543f13ebda91391626ad08c67c.png

Описание ВАХ#

Как видно из ВАХ с увеличением тока начиная от нуля до 0.8 значения напряжение равна нулю (стационарный эффект Джозефсона). При достижении значения тока 0.8 появляется напряжение и далее с ростом тока растет и напряжение. Как было отмечено в описании модели ток при котором появляется напряжение назывется критическим током. Теперь рассмотрим как критический ток меняется с изменением внешнего потока.

Исследование влияния \(\varphi_{ext}\) на ВАХ#

Проведем вычисления при различных значений \(\varphi_{ext}\), например при \(\varphi_{ext}=0\), \(\varphi_{ext}=0.25\) и \(\varphi_{ext}=0.5\). Численное моделирование проводилось в интервале времени [0, 1000] с шагом 0.05.

ph_ext = 0
start = time.time()
res0 = comput_vax(s0, beta_c, beta_L, Pi, n,
                  Icurr, ph_ext, nt, ntmin, t0, deltat, a, deltaI)
end = time.time()
print(end-start)
ph_ext = 0.25
start = time.time()
res1 = comput_vax(s0, beta_c, beta_L, Pi, n,
                  Icurr, ph_ext, nt, ntmin, t0, deltat, a, deltaI)
end = time.time()
print(end-start)
ph_ext = 0.5
start = time.time()
res2 = comput_vax(s0, beta_c, beta_L, Pi, n,
                  Icurr, ph_ext, nt, ntmin, t0, deltat, a, deltaI)
end = time.time()
print(end-start)

График ВАХ#

fig = plt.figure(figsize=(9, 8))
plt.plot(res0[:, 0], res0[:, 1], label='ВАХ phi_ext=0',  linewidth=3.0)
plt.plot(res1[:, 0], res1[:, 1], label='ВАХ phi_ext=0.25',  linewidth=3.0)
plt.plot(res2[:, 0], res2[:, 1], label='ВАХ phi_ext=0.5',  linewidth=3.0)
plt.xlabel('I', size=12)
plt.ylabel('V', size=12)
plt.legend(loc='upper left')
plt.savefig('cvcsquid.png')
plt.show()
_images/f94003af4d2c0a926de686227ed7275f5122cd2fa7101d0978c0173dc32600a1.png

Как видно из графика ВАХ при \(\varphi_{ext}=0\) критический ток равен \(I_{c}=2\), при \(\varphi_{ext}=0.25\) - \(I_{c}=1.58\), а при \(\varphi_{ext}=0.5\) - \(I_{c}=0.8\), т.е. с изменением \(\varphi_{ext}\) меняется величина критического тока.

3. Вычисление зависимости критического тока от внешнего магнитного поля#

Создаем фукнцию getIc.#

Для этого вводим следующий параметр

  • epsilon - точность критического тока

ph_ext = 0.5
epsilon = 0.001
Icurr = 0
@njit
def get_Ic(s0, beta_c, beta_L, Pi, n, ph_ext,
           nt, ntmin, t0, deltat, deltaI, epsilon):
    ''' Функция для вычисления значении критического тока
            при заданных параметров. Возвращает для заданного
            значения параметров значение критического тока.
        s0 - массив начальных условий,
        beta_c, beta_L, Pi, n, Icurr, ph_ext,-параметры модели
        nt - количество точек по времени, в которых найдены решение,
        ntmin - номер шага по времени соответсвующее
                нижней границе интегрирования,
        t0-начальное значение времени,
        deltat - шаг по времени
        deltaI - шаг по току
        epsilon -  точность нахождения критического тока
        '''
    Icurr = 0
    a = 1
    while Icurr < 100:
        res = cvcpoint_new(s0, beta_c, beta_L, Pi, n,
                           Icurr, ph_ext, nt, ntmin, t0, deltat)
        Vav = res[0]
        s0 = res[1:]
        if(Vav > epsilon):
            Ic = Icurr
            break
        Icurr += deltaI
    return Ic
epsilon = 0.01
ph_ext = 1
t_start = time.time()
k = get_Ic(s0, beta_c, beta_L, Pi, n, ph_ext,
           nt, ntmin, t0, deltat, deltaI, epsilon)
t_finish = time.time()
print(f'Execution time {t_finish - t_start} s')
print(k)

Исследование зависимости критического тока от внешнего магнитного поля#

Для этого вводим следующие параметры:

  • npoint - количество точек в интервале значений внешнего поля

  • ph_extmin - минимальное значение внешнего поля

  • ph_extmax - максимальное значение внешнего поля

  • deltaph_ext - шаг изменения внешнего поля

npoint = 51
ph_extmin = 0.0
ph_extmax = 2.0
deltaph_ext = (ph_extmax - ph_extmin) / (npoint-1)
print(deltaph_ext)

Создаем массив значений внешнего поля для которых нужно вычислить критическй ток

ph_ext_array = np.linspace(ph_extmin, ph_extmax, num=npoint)
ph_ext_array.shape
(51,)

Создаем пустой массив для значений крического тока

Ic_array = np.zeros(npoint)
Ic_array.shape
(51,)

Создаем функцию зависимости критического тока от внешнего поля#

@njit
def funk_numba(j, ph_extmin, deltaph_ext, s0, beta_c, beta_L, Pi, n, ph_ext,
               nt, ntmin, t0, deltat, deltaI, epsilon):
    ''' Функция для вычисления значении критического тока
            при заданных параметров и фиксированном значении
            потока внешнего магнитного поля с номером j
            в интервале [ph_extmin, j*deltaph_ext]. Возвращает для
            заданного значения ph_ext значение критического тока.
        s0 - массив начальных условий,
        beta_c, beta_L, Pi, n, Icurr, ph_ext,-параметры модели
        nt - количество точек по времени, в которых найдены решение,
        ntmin - номер шага по времени соответсвующее
                нижней границе интегрирования,
        t0-начальное значение времени,
        deltat - шаг по времени
        deltaI - шаг по току
        epsilon - точность критического тока
        '''
    ph_ext = ph_extmin + deltaph_ext * j
    Ic = get_Ic(s0, beta_c, beta_L, Pi, n, ph_ext,
                nt, ntmin, t0, deltat, deltaI, epsilon)
    return Ic
epsilon = 0.1
%%time
for i in range(0, npoint):
    Icrit = funk_numba(i, ph_extmin, deltaph_ext, s0, beta_c, beta_L, Pi, n, ph_ext,
                       nt, ntmin, t0, deltat, deltaI, epsilon)
    Ic_array[i] = Icrit
print(Ic_array)

Посторим график зависимости критического тока от внешнего поля#

fig = plt.figure(figsize=(6, 6))
plt.plot(ph_ext_array, Ic_array,
         label='Зависимость $I_c$ от phi_ext', linewidth=3.0)
plt.xlabel('$phi_{ext}$', size=12)
plt.ylabel('$I_{c}$', size=12)
plt.legend(loc='upper right')
plt.savefig('Icfield.png')
plt.show()
_images/b3e5aa7c65f8b4877988709548dfdf49e45ecf18a0e5318c1c4598ab63320e7e.png

Данный график демонстрирует модулирование критического тока с изменением \(\varphi_{ext}\).

Параллелизация с numba#

Материалы документации Numba

Setting the Number of Threads
The number of threads used by numba is based on the number of CPU cores available (see numba.config.NUMBA_DEFAULT_NUM_THREADS), but it can be overridden with the NUMBA_NUM_THREADS environment variable.

https://numba.readthedocs.io/en/stable/developer/threading_implementation.html

from numba import config, njit, threading_layer
from numba import set_num_threads, get_num_threads
# set the threading layer before any parallel target compilation
config.THREADING_LAYER = 'threadsafe'
1:80: E501 line too long (81 > 79 characters)
config.NUMBA_DEFAULT_NUM_THREADS
88
set_num_threads(5)
config.NUMBA_NUM_THREADS = 20
config.NUMBA_NUM_THREADS
20
config.THREADING_LAYER = 'omp'
threading_layer()
'tbb'
config.THREADING_LAYER
'omp'
# demonstrate the threading layer chosen
print("Threading layer chosen: %s" % threading_layer())
get_num_threads()
5
set_num_threads(5)
get_num_threads()
5
import os
print('NUMBA NUM THREADS')
try:
    print(os.environ['NUMBA_NUM_THREADS'])
except:
    print(config.NUMBA_DEFAULT_NUM_THREADS)
5:1: E722 do not use bare 'except'
import numba as nb
from numba import prange
@njit(parallel=True)
def comput_parallel(npoint, ph_extmin, deltaph_ext, s0, beta_c, beta_L, Pi, n,
                    ph_ext, nt, ntmin, t0, deltat, deltaI, epsilon):
    ''' Функция для вычисления зависимости критического тока
            от потока внешнего магнитного поля при заданных параметров.
            Возвращает массив значений критических токов для заданного
            интервала значений ph_ext.
        npoint - количество точек ph_ext в интервале
        ph_extmin - начало интервала потока внешнего магнитного поля,
        deltaph_ext - шаг потока внешнего магнитного поля,
        s0 - массив начальных условий,
        beta_c, beta_L, Pi, n, Icurr, ph_ext,-параметры модели
        nt - количество точек по времени, в которых найдены решение,
        ntmin - номер шага по времени соответсвующее
                нижней границе интегрирования,
        t0-начальное значение времени,
        deltat - шаг по времени
        deltaI - шаг по току
        epsilon - точность критического тока
        '''
    Ic_array = np.empty(npoint)
    Ic_array[:] = 0
    for i in nb.prange(npoint):
        Ic = funk_numba(i,  ph_extmin, deltaph_ext, s0, beta_c, beta_L, Pi, n,
                        ph_ext, nt, ntmin, t0, deltat, deltaI, epsilon)
        Ic_array[i] = Ic
    return Ic_array
%%time
Ic_array  = comput_parallel(npoint, ph_extmin, deltaph_ext, s0, beta_c, beta_L, Pi, n, ph_ext, nt, ntmin, t0, deltat, deltaI, epsilon)
fig = plt.figure(figsize=(6, 6))
plt.plot(ph_ext_array, Ic_array,
         label='Зависимость Ic от phi_ext', linewidth=3.0)
plt.xlabel('$phi_{ext}$', size=12)
plt.ylabel('$I_{c}$', size=12)
plt.legend(loc='upper right')
plt.show()
_images/d2d535e3054207b3185b35c4a6564ead76e2c2df8b3cd582bca975a46639f7b9.png

Вычисления при разных чисел потоков.

На этом этапе реализована параллелизация вычислении критического тока. На графике представлена рассчитанная зависимость критического тока СКВИДа от потока \(\varphi_{ext}\) в интервале потока [0, 2]. Данная зависимость демонстрирует модулирование критического тока с изменением \(\varphi_{ext}\), что согласуется с известными результатами. Теперь можно вычислить зависимость времени вычисления от числа вычислительных потоков.

set_num_threads(1)
t_start = time.time()
Ic_array  = comput_parallel(npoint, ph_extmin, deltaph_ext, s0, beta_c, beta_L, Pi, n, ph_ext, nt, ntmin, t0, deltat, deltaI, epsilon)
t_finish = time.time()
print(f'Execution time {t_finish - t_start} s')
Execution time 51.1058554649353 s
set_num_threads(5)
t_start = time.time()
Ic_array  = comput_parallel(npoint, ph_extmin, deltaph_ext, s0, beta_c, beta_L, Pi, n, ph_ext, nt, ntmin, t0, deltat, deltaI, epsilon)
t_finish = time.time()
print(f'Execution time {t_finish - t_start} s')
Execution time 13.315101623535156 s
set_num_threads(10)
t_start = time.time()
Ic_array  = comput_parallel(npoint, ph_extmin, deltaph_ext, s0, beta_c, beta_L, Pi, n, ph_ext, nt, ntmin, t0, deltat, deltaI, epsilon)

t_finish = time.time()
print(f'Execution time {t_finish - t_start} s')
Execution time 7.867552280426025 s

Построим график зависимости времени вычисления от числа потоков

Proc=[1,5,10,15,20,25,30,35,40,45,50,55,60,65,70]
Time=[51.1,13.31,7.86,5.65,4.9,5.05,8.5,8.53,9.9,9.81,11.91,11.35,11.37,11.65,11.56]
fig = plt.figure(figsize=(6, 6))
plt.plot(Proc, Time, label='Calculation time from number of Threads', linewidth=3.0)
plt.xlabel('Threads number', size=12)
plt.ylabel('Time (s)', size=12)
plt.legend(loc='upper right')
plt.savefig('calc_time.png')
plt.show()
_images/dc99dfb40017a876229108321998a5123998c9d88574a09ab3d063127cef051b.png

Разработанный инструментарий позволяет моделировать физические свойства сверхпроводникового квантового интерферометра с двумя джозефсоновскими переходами (СКВИД постоянного тока). Реализованы алгоритмы для вычисления вольт-амперной характеристики СКВИДа под воздействием внешнего магнитного поля и зависимости критического тока СКВИДа от внешнего магнитного поля. При программной реализации разработанных алгоритмов использовались функции библиотеки Numba, в том числе механизм распараллеливания при вычислении зависимости критического тока от потока внешнего магнитного поля. Представленные результаты были получены на экосистеме ML/DL/HPC Гетерогенной платформы HybriLIT (ЛИТ ОИЯИ).

Работа выполнена при финансовой поддержки РНФ в рамках проекта No 22-71-10022.