11#!/usr/bin/env python3
22# -*- coding: utf-8 -*-
33""":py:class:`lmfit.Model` model classes and functions for various superconductivity related models."""
4+
45# pylint: disable=invalid-name
56# This module can be used with Stoner v.0.9.0 asa standalone module
67__all__ = [
@@ -59,36 +60,41 @@ def _strijkers_core(V, omega, delta, P, Z):
5960 This version only uses 1 delta, not modified for proximity
6061 """
6162 # Parameters
62- E = np .linspace (- 2 * np .max (np .abs (V )), 2 * np .max (np .abs (V )), V .size * 20 )
63- gauss = np .exp (- (E ** 2 ) / (2 * omega ** 2 ))
64- gauss /= gauss .sum ()
65-
66- absE = np .abs (E )
67- E2 = E ** 2
68- delta2 = delta ** 2
69- Z2 = Z ** 2
70- Zfac = 1 + 2 * Z2
71- denom = np .sqrt (E2 - delta2 )
72- eps = absE / denom
73-
74- Au1 = delta2 / (E2 + (delta2 - E2 ) * Zfac ** 2 )
75- eps2 = eps ** 2
76- Au2 = (eps2 - 1 ) / (eps + Zfac ) ** 2
77- Bu2 = 4 * Z2 * (1 + Z2 ) / (eps + Zfac ) ** 2
78- Bp2 = Bu2 / (1 - Au2 )
79-
80- up = (1 - P ) * (1 + Z2 )
81- pp = P * (1 + Z2 )
82-
83- G = up + pp + np .where (absE <= delta , up * (2 * Au1 - 1 ) - pp , up * (Au2 - Bu2 ) - pp * Bp2 )
84-
85- conv = np .convolve (G , gauss )
86- conv = conv [(E .size // 2 ) : (3 * E .size // 2 )]
87-
88- idx = np .searchsorted (E , V )
89- El , Er = E [idx - 1 ], E [idx ]
90- Gl , Gr = conv [idx - 1 ], conv [idx ]
91- return (Gr - Gl ) / (Er - El ) * (V - El ) + Gl
63+ E = np .linspace (
64+ - 2 * np .max (np .abs (V )), 2 * np .max (np .abs (V )), V .size * 20
65+ ) # Energy range in meV - we use a mesh 20x denser than data points
66+ gauss = np .exp (- (E ** 2 / (2 * omega ** 2 )))
67+ gauss /= gauss .sum () # Normalised gaussian for the convolution
68+
69+ U = (
70+ (delta ** 2 ) / ((E ** 2 ) + (((delta ** 2 ) - (E ** 2 )) * (1 + 2 * (Z ** 2 )) ** 2 )),
71+ (((np .abs (E ) / (np .sqrt ((E ** 2 ) - (delta ** 2 )))) ** 2 ) - 1 )
72+ / (((np .abs (E ) / (np .sqrt ((E ** 2 ) - (delta ** 2 )))) + (1 + 2 * (Z ** 2 ))) ** 2 ),
73+ (4 * (Z ** 2 ) * (1 + (Z ** 2 ))) / (((np .abs (E ) / (np .sqrt ((E ** 2 ) - (delta ** 2 )))) + (1 + 2 * (Z ** 2 ))) ** 2 ),
74+ )
75+
76+ # Optimised for a single use of np.where
77+ G = (
78+ (1 - P ) * (1 + (Z ** 2 ))
79+ + 1 * (P ) * (1 + (Z ** 2 ))
80+ + + np .where (
81+ np .abs (E ) <= delta ,
82+ (1 - P ) * (1 + (Z ** 2 )) * (2 * U [0 ] - 1 ) - np .ones_like (E ) * 1 * (P ) * (1 + (Z ** 2 )),
83+ (1 - P ) * (1 + (Z ** 2 )) * (U [1 ] - U [2 ]) - (U [2 ] / (1 - U [1 ])) * 1 * (P ) * (1 + (Z ** 2 )),
84+ )
85+ )
86+
87+ # Convolve and chop out the central section
88+ cond = np .convolve (G , gauss )
89+ cond = cond [(E .size // 2 ) : 3 * (E .size // 2 )]
90+ # Linear interpolation back onto the V data point
91+ matches = np .searchsorted (E , V )
92+ condl = cond [matches - 1 ]
93+ condh = cond [matches ]
94+ El = E [matches - 1 ]
95+ Er = E [matches ]
96+ cond = (condh - condl ) / (Er - El ) * (V - El ) + condl
97+ return cond
9298
9399
94100def strijkers (V , omega , delta , P , Z ):
@@ -259,7 +265,7 @@ def icRN_Clean(d_f, IcRn0, E_x, v_f, d_0):
259265 return IcRn0 * np .abs (np .sin (A * x )) / (A * x )
260266
261267
262- def ic_RN_Dirty (d_f , IcRn0 , E_x , v_f , d_0 , tau , delta , T ):
268+ def ic_RN_Dirty (d_f , IcRn0 , E_x , v_f , d_0 , tau , delta , T ): # pylint: disable=unused-argument
263269 r"""Critical Current versus ferromagnetic narrier thickness, clean limit.
264270
265271 Args:
@@ -285,12 +291,21 @@ def ic_RN_Dirty(d_f, IcRn0, E_x, v_f, d_0, tau, delta, T):
285291 """
286292 L = v_f * tau * 1e-9
287293 t = tau / hbar
288- w_m = lambda m : (2 * m + 1 ) * T * np .pi * kb
289- k_m = lambda m : (1 + 2 * np .abs (w_m (m )) * t ) + (0 - 2j ) * E_x * t
290- integrad = lambda mu , m : np .real (mu / (np .sinh (d_f * k_m (m ) / (mu * L ))))
291- prefactor = lambda m : delta ** 2 / (delta ** 2 + w_m (m ) ** 2 )
292294
293- term = lambda m : prefactor (m ) * quad (integrad , - 1 , 1 , (m ,)) # pylint: disable=W0612
295+ def _w_m (m ):
296+ return (2 * m + 1 ) * T * np .pi * kb
297+
298+ def _k_m (m ):
299+ return (1 + 2 * np .abs (_w_m (m )) * t ) + (0 - 2j ) * E_x * t
300+
301+ def _integrad (mu , m ):
302+ return np .real (mu / (np .sinh (d_f * _k_m (m ) / (mu * L ))))
303+
304+ def _prefactor (m ):
305+ return delta ** 2 / (delta ** 2 + _w_m (m ) ** 2 )
306+
307+ def term (m ): # pylint: disable=unused-variable
308+ return _prefactor (m ) * quad (_integrad , - 1 , 1 , (m ,)) # pylint
294309
295310
296311class Strijkers (Model ):
@@ -324,7 +339,7 @@ def __init__(self, *args, **kwargs):
324339 """Configure Initial fitting function."""
325340 super ().__init__ (strijkers , * args , ** kwargs )
326341
327- def guess (self , data , ** kwargs ): # pylint: disable=unused-argument
342+ def guess (self , data , x = None , ** kwargs ): # pylint: disable=unused-argument
328343 """Guess starting values for a good Nb contact to a ferromagnet at 4.2K."""
329344 pars = self .make_params (omega = 0.5 , delta = 1.50 , P = 0.42 , Z = 0.15 )
330345 pars ["omega" ].min = 0.36
@@ -336,6 +351,10 @@ def guess(self, data, **kwargs): # pylint: disable=unused-argument
336351 pars ["P" ].max = 1.0
337352 return update_param_vals (pars , self .prefix , ** kwargs )
338353
354+ def copy (self , ** kwargs ):
355+ """Make a new copy of the model."""
356+ return self .__class__ (** kwargs )
357+
339358
340359class RSJ_Noiseless (Model ):
341360 r"""Implement a simple noiseless RSJ model.
@@ -367,9 +386,8 @@ def __init__(self, *args, **kwargs):
367386 """Configure Initial fitting function."""
368387 super ().__init__ (rsj_noiseless , * args , ** kwargs )
369388
370- def guess (self , data , ** kwargs ):
389+ def guess (self , data , x = None , ** kwargs ):
371390 """Guess parameters as gamma=2, H_k=0, M_s~(pi.f)^2/(mu_0^2.H)-H."""
372- x = kwargs .get ("x" , np .linspace (1 , len (data ), len (data ) + 1 ))
373391
374392 v_offset_guess = np .mean (data )
375393 v = np .abs (data - v_offset_guess )
@@ -389,6 +407,10 @@ def guess(self, data, **kwargs):
389407 pars ["Ic_n" ].max = 0
390408 return update_param_vals (pars , self .prefix , ** kwargs )
391409
410+ def copy (self , ** kwargs ):
411+ """Make a new copy of the model."""
412+ return self .__class__ (** kwargs )
413+
392414
393415class RSJ_Simple (Model ):
394416 r"""Implements a simple noiseless symmetric RSJ model.
@@ -420,9 +442,8 @@ def __init__(self, *args, **kwargs):
420442 """Configure Initial fitting function."""
421443 super ().__init__ (rsj_simple , * args , ** kwargs )
422444
423- def guess (self , data , ** kwargs ):
445+ def guess (self , data , x = None , ** kwargs ):
424446 """Guess parameters as gamma=2, H_k=0, M_s~(pi.f)^2/(mu_0^2.H)-H."""
425- x = kwargs .get ("x" , np .linspace (1 , len (data ), len (data ) + 1 ))
426447
427448 v_offset_guess = np .mean (data )
428449 v = np .abs (data - v_offset_guess )
@@ -441,6 +462,10 @@ def guess(self, data, **kwargs):
441462 # pars["Ic"].min = 0
442463 return update_param_vals (pars , self .prefix , ** kwargs )
443464
465+ def copy (self , ** kwargs ):
466+ """Make a new copy of the model."""
467+ return self .__class__ (** kwargs )
468+
444469
445470class Ic_B_Airy (Model ):
446471 r"""Critical Current for a round Josepshon Junction wrt to Field.
@@ -478,9 +503,8 @@ def __init__(self, *args, **kwargs):
478503 """Configure Initial fitting function."""
479504 super ().__init__ (ic_B_airy , * args , ** kwargs )
480505
481- def guess (self , data , ** kwargs ):
506+ def guess (self , data , x = None , ** kwargs ):
482507 """Guess parameters as max(data), x[argmax(data)] and from FWHM of peak."""
483- x = kwargs .get ("x" , np .linspace (- len (data ) / 2 , len (data ) / 2 , len (data )))
484508
485509 Ic0_guess = data .max ()
486510 B_offset_guess = x [data .argmax ()]
@@ -491,3 +515,7 @@ def guess(self, data, **kwargs):
491515 pars = self .make_params (Ic0 = Ic0_guess , B_offset = B_offset_guess , A = A_guess )
492516 pars ["Ic0" ].min = 0
493517 return update_param_vals (pars , self .prefix , ** kwargs )
518+
519+ def copy (self , ** kwargs ):
520+ """Make a new copy of the model."""
521+ return self .__class__ (** kwargs )
0 commit comments