test_solver.py 6.96 KiB
# -*- coding:utf-8; eval: (blacken-mode) -*-
import os
import pytest
import numpy as np
from numpy.testing import assert_allclose
from numpy import pi
from scipy.special import zeta
from usadelndsoc.solver import *
from usadelndsoc.bcs import BCS_Delta
try:
import usadel1
except ImportError:
usadel1 = None
def example_sns_1d(phi, L, nx=500, ny=1, Delta0=1.0, alpha=0.0):
solver = Solver(nx=nx, ny=ny)
solver.mask[...] = MASK_NONE
solver.mask[0, :] = MASK_TERMINAL
solver.mask[-1, :] = MASK_TERMINAL
solver.Lx = L
solver.Ly = L
solver.alpha = alpha
solver.Omega[...] = 0
solver.Delta[0, :] = Delta0 * np.exp(-1j * phi / 2) * np.eye(2)
solver.Delta[-1, :] = Delta0 * np.exp(+1j * phi / 2) * np.eye(2)
return solver
def test_example_sns_1d():
# Test against analytic solution
omega = 30
Lx = 10
phi = pi / 2
sol = example_sns_1d(phi, Lx)
res = sol.solve(omega=omega)
x = np.linspace(0, Lx, sol.shape[0])
theta0 = np.arctan(1 / omega)
theta_a = 4 * np.arctan(np.exp(-np.sqrt(2 * omega) * x) * np.tan(theta0 / 4))
theta_b = 4 * np.arctan(np.exp(-np.sqrt(2 * omega) * (Lx - x)) * np.tan(theta0 / 4))
F = np.sin(theta_a) * np.exp(-1j * phi / 2) + np.sin(theta_b) * np.exp(1j * phi / 2)
rF = res.F[:, :, 0, 0]
assert_allclose(rF.squeeze(), F, rtol=0, atol=5e-3 * abs(F).max())
@pytest.mark.skipif(usadel1 is None, reason="usadel1 not installed")
def test_example_sns_1d_J_usadel1():
Lx = 10
phi = pi / 2
sol = example_sns_1d(phi, Lx)
sol.Ly = 1
T = 0.2
Delta, I0, _, success = sol.self_consistency(T=T, T_c0=0)
tau3 = np.diag([1, 1, -1, -1])
I0 = (I0[:-1, 0, 0] @ tau3).trace(axis1=-2, axis2=-1)
I0_mean = I0.real.mean()
# Current is real-valued and conserved
assert_allclose(I0.imag, 0, atol=1e-8)
assert_allclose(I0.real, I0_mean, rtol=5e-3)
# Compare to Usadel1
g = usadel1.Geometry(1, 2)
g.t_type = [usadel1.NODE_CLEAN_S_TERMINAL, usadel1.NODE_CLEAN_S_TERMINAL]
g.w_type = usadel1.WIRE_TYPE_N
g.w_ends[0, :] = [0, 1]
g.w_length = Lx
g.w_conductance = 1
g.t_delta = [1, 1]
g.t_phase = [-phi / 2, phi / 2]
g.t_t = T
g.t_mu = 0
s = usadel1.CurrentSolver(g, maxE=4, ne=2000)
s.solve()
I1, _ = s.get_currents()
I1_mean = I1.mean()
assert_allclose(I0_mean, I1_mean, rtol=5e-3)
@pytest.mark.skipif(usadel1 is None, reason="usadel1 not installed")
def test_example_sss_1d_J_usadel1():
Lx = 10
phi = pi / 2
sol = example_sns_1d(phi, 30 * Lx / (30 - 2), nx=30)
sol.Ly = 1
T_c0 = np.exp(np.euler_gamma) / np.pi
T = 0.2
Delta, I0, _, success = sol.self_consistency(T=T, T_c0=T_c0, workers=-1)
tau3 = np.diag([1, 1, -1, -1])
I0 = (I0[:-1, 0, 0] @ tau3).trace(axis1=-2, axis2=-1)
I0_mean = I0.real.mean()
# Current is real-valued and conserved
assert_allclose(I0.imag, 0, atol=1e-8)
assert_allclose(I0.real, I0_mean, rtol=5e-3)
# Compare to Usadel1
g = usadel1.Geometry(1, 2)
g.t_type = [usadel1.NODE_CLEAN_S_TERMINAL, usadel1.NODE_CLEAN_S_TERMINAL]
g.w_type = usadel1.WIRE_TYPE_S
g.w_ends[0, :] = [0, 1]
g.w_delta = 1.0
g.w_phase = np.linspace(-phi / 2, phi / 2, len(g.x))
g.w_length = Lx
g.w_conductance = 1
g.t_delta = [1, 1]
g.t_phase = [-phi / 2, phi / 2]
g.t_t = T
g.t_mu = 0
g.omega_D = 100
g.coupling_lambda = 1 / np.arccosh(g.omega_D / 1.0)
for k, d, v in usadel1.self_consistent_matsubara_iteration(g):
if d.relative_residual_norm() < 1e-6:
break
s = usadel1.CurrentSolver(g, ne=2000)
s.solve()
I1, _ = s.get_currents()
I1_mean = I1.mean()
assert_allclose(I0_mean, I1_mean, rtol=5e-2)
OMEGAS = [30, 5, (0.3 + 0.001j) / 1j]
@pytest.mark.skipif(usadel1 is None, reason="usadel1 not installed")
@pytest.mark.parametrize("omega", OMEGAS)
def test_example_sns_usadel1(omega):
# Test against another numerical solution
Lx = 10
phi = pi / 2
sol = example_sns_1d(phi, Lx)
res = sol.solve(omega=omega)
g = usadel1.Geometry(1, 2)
g.t_type = [usadel1.NODE_CLEAN_S_TERMINAL, usadel1.NODE_CLEAN_S_TERMINAL]
g.w_type = usadel1.WIRE_TYPE_N
g.w_ends[0, :] = [0, 1]
g.w_length = Lx
g.w_conductance = 1
g.t_delta = [1, 1]
g.t_phase = [-phi / 2, phi / 2]
g.t_t = 1
g.t_mu = 0
s = usadel1.Solver()
s.set_geometry(g)
x_scl = np.linspace(0, 1, sol.shape[0])
res2 = s.sp_solve(np.array([1j * omega]), x_scl)
F2 = 2j * res2.a / (1 - res2.a * res2.b)
F2c = 2j * res2.b / (1 - res2.a * res2.b)
assert_allclose(
res.F[..., 0, 0].squeeze(), F2.squeeze(), rtol=1e-2, atol=1e-3 * abs(F2).max()
)
assert_allclose(
res.Fc[..., 0, 0].squeeze(),
F2c.squeeze(),
rtol=1e-2,
atol=1e-3 * abs(F2c).max(),
)
@pytest.mark.slow
@pytest.mark.parametrize("T", [0.28, 0.53, 0.8])
def test_selfcons_kupriyanov(request, T):
fns_k = {
0.28: "kupriyanov1981-cpr-a1.dat",
0.53: "kupriyanov1981-cpr-a2.dat",
0.8: "kupriyanov1981-cpr-a3.dat",
}
fns_u = {
0.28: "L3.989_T0.28_TcR1_TcW1_gamma1e-06_r0_ns7.dat",
0.53: "L3.989_T0.53_TcR1_TcW1_gamma1e-06_r0_ns7.dat",
0.8: "L3.989_T0.8_TcR1_TcW1_gamma1e-06_r0_ns7.dat",
}
fn_k = os.path.join(os.path.dirname(__file__), "data", fns_k[T])
fn_u = os.path.join(os.path.dirname(__file__), "data", fns_u[T])
L = 3.989
Tc0 = 1.0
Delta0 = BCS_Delta(T, Tc0)
Delta00 = BCS_Delta(0.05, Tc0)
# L is the distance between terminals, but Lx the system size where
# the two outermost layers are terminals
nx = 40
Lp = (L / (nx - 2)) * nx
sol = example_sns_1d(0, L, nx=nx, ny=1, Delta0=Delta0)
sol.Ly = 1
phase_mask = (sol.mask == MASK_TERMINAL) & (sol.x[:, None] > 0)
dn = request.config.cache.makedir("test_selfcons_kupriyanov")
cache_fn = os.path.join(dn, f"T-{T}.npz")
res = cpr(
sol,
T=T,
T_c0=Tc0,
phase_mask=phase_mask,
filename=cache_fn,
workers=-1,
ds=0.4,
)
phis, Deltas, Js = res
tau3 = np.diag([1, 1, -1, -1])
Js = (Js @ tau3).trace(axis1=-2, axis2=-1)
Is = Js[:, :-1, 0, 0].squeeze().mean(axis=1)
m = ~np.isnan(Is)
Is = Is[m]
phis = phis[m]
phis_k, Is_k = np.loadtxt(fn_k).T
phis_u, Is_u = np.loadtxt(fn_u).T
Is_k *= Delta00 / L
Imax = abs(Is_u).max()
assert curve_separation(phis, Is, phis_u, Is_u) < 0.04 * Imax
assert curve_separation(phis, Is, phis_k, Is_k) < 0.075 * Imax
def curve_separation(x1, y1, x2, y2):
# Resample
k1 = np.arange(len(x1))
k1p = np.linspace(k1[0], k1[-1], 2000)
k2 = np.arange(len(x2))
k2p = np.linspace(k2[0], k2[-1], 2000)
x1 = np.interp(k1p, k1, x1)
y1 = np.interp(k1p, k1, y1)
x2 = np.interp(k2p, k2, x2)
y2 = np.interp(k2p, k2, y2)
# Min-max separation
s = np.hypot(abs(x1[:, None] - x2[None, :]), abs(y1[:, None] - y2[None, :]))
return s.min(axis=1).max()