No title

.\MR-WLmodel.py

import sys
import os
import re
import csv
import numpy as np
from math import log, exp
import scipy.special
from scipy import optimize
from scipy.optimize import minimize
from matplotlib import pyplot as plt

pi = 3.14159265358979323846
h = 6.6260755e-34 # Js";
hbar = 1.05459e-34 # "Js";
c = 2.99792458e8 # m/s";
e = 1.60218e-19 # C";
e0 = 8.854418782e-12; # C2N-1m-2";
kB = 1.380658e-23 # JK-1";
me = 9.1093897e-31 # kg";

nm2m = 1.0e-9
kMR = e * e / pi / h # convert delta_sigma to sheet conductance S


# parameters
# mode: 'sim', 'fit'
mode = 'sim'

# simulation B range
Bmin = -5.0
Bmax = 5.0
nB = 101
Bth = 1.0e-10
Bstep = (Bmax - Bmin) / (nB - 1)

# Files. Resistivity is in ohm*m
file_xx = 'MRxx.csv'
file_xy = 'MRxy.csv'
basename = os.path.basename(file_xx)
dirname = os.path.dirname(file_xx)
header, ext = os.path.splitext(file_xx)
filebody = os.path.basename(file_xx)
output_csv = os.path.join(dirname, filebody + '-fitting.csv')
output_txt = os.path.join(dirname, filebody + '-fitting.txt')
thickness = 5.0 # in nm

headerxx = []
Bxx = [] # magnetic field in T
MRxx = [] # resistivity in ohm*m
MCxx = [] # sheet conductivity in S

# fitting B range
Bxxmin = -5.0
Bxxmax = 5.0

# optimization parameters
sigma00 = 0.0 # sheet conductance at B = 0 in S
alpha0 = 1.0 # prefactor of weak (anti-)localization model. 1 for WL, -1 for AWL
mfp0 = 20.0 # Mean free path in nm

# initial parameter list
ai0 = [sigma00, alpha0, mfp0]
optid = [ 1, 0, 1]

#=============================================
# scipy.optimize.minimizeで使うアルゴリズム
#=============================================
#nelder-mead Downhill simplex
#powell Modified Powell
#cg conjugate gradient (Polak-Ribiere method)
#bfgs BFGS法
#newton-cg Newton-CG
#trust-ncg 信頼領域 Newton-CG 法
#dogleg 信頼領域 dog-leg 法
#L-BFGS-B’ (see here)
#TNC’ (see here)
#COBYLA’ (see here)
#SLSQP’ (see here)
#trust-constr’(see here)
#dogleg’ (see here)
#trust-exact’ (see here)
#trust-krylov’ (see here)
method = "nelder-mead"
#method = 'cg'
#method = 'powell'
#method = 'bfgs'

maxiter = 1000
tol = 1.0e-6
h_diff = 1.0e-3

# graph variabls
fig = None


# 実数値に変換できない文字列をfloat()で変換するとエラーになってプログラムが終了する
# この関数は、変換できなかったらNoneを返すが、プログラムは終了させない
def pfloat(str):
    try:
        return float(str)
    except:
        return None

# pfloat()のint版
def pint(str):
    try:
        return int(str)
    except:
        return None

# 起動時引数を取得するsys.argリスト変数は、範囲外のindexを渡すとエラーになってプログラムが終了する
# egtarg()では、範囲外のindexを渡したときは、defvalを返す
def getarg(position, defval = None):
    try:
        return sys.argv[position]
    except:
        return defval

# 起動時引数を実数に変換して返す
def getfloatarg(position, defval = None):
    return pfloat(getarg(position, defval))

# 起動時引数を整数値に変換して返す
def getintarg(position, defval = None):
    return pint(getarg(position, defval))

def usage(sigma0 = sigma00, alpha = alpha0, mfp = mfp0):
    global Bmin, Bmax, nB
    global file_xx, file_xy, thickness
    global Bxxmin, Bxxmax

    print("")
    print("Usage: Variables in () are optional")
    print(" MFP in nm B in T thickness in nm")
    print(" (i) python {} sim (thickness sigma0 alpha mfp Bmin Bmax nB"
            .format(sys.argv[0]))
    print(" ex: python {} sim {} {} {} {} {} {} {}"
            .format(sys.argv[0], thickness, sigma0, alpha, mfp, Bmin, Bmax, nB))
    print(" (ii) python {} fit (file_xx thickness sigma0 id alpha id MFP id Bxxmin Bxxmax)"
            .format(sys.argv[0]))
    print(" ex: python {} fit {} {} {} {} {} {} {} {} {} {}"
            .format(sys.argv[0], file_xx, thickness, sigma0, optid[0], alpha, optid[1], mfp, optid[2], Bxxmin, Bxxmax))

def terminate(message = None):
    if message is not None:
        print("")
        print(message)

    usage()
    print("")
    exit()

# 起動時引数を使ってglobal変数を更新する
def updatevars():
    global mode
    global Bmin, Bmax, Bstep, nB
    global file_xx, thickness, Bxxmin, Bxxmax
    global sigma00, alpha0, mfp0
    global ai0, optid

    argv = sys.argv
    if len(argv) == 1:
        terminate()

    mode = getarg( 1, mode)
    if mode == 'sim':
        thickness = getfloatarg( 2, thickness)
        sigma00 = getfloatarg( 3, sigma00)
        alpha0 = getfloatarg( 4, alpha0)
        mfp0 = getfloatarg( 5, mfp0)
        Bmin = getfloatarg( 6, Bmin)
        Bmax = getfloatarg( 7, Bmax)
        nB = getintarg ( 8, nB)
        Bstep = (Bmax - Bmin) / (nB - 1)
    elif mode == 'fit':
        file_xx = getarg ( 2, file_xx)
        thickness = getfloatarg( 3, thickness)
        sigma00 = getfloatarg( 4, sigma00)
        optid[0] = getintarg ( 5, optid[0])
        alpha0 = getfloatarg( 6, alpha0)
        optid[1] = getintarg ( 7, optid[1])
        mfp0 = getfloatarg( 8, mfp0)
        optid[2] = getintarg ( 9, optid[2])
        Bxxmin = getfloatarg(10, Bmin)
        Bxxmax = getfloatarg(11, Bmax)
    else:
        terminate("Error: Invalid mode [{}]".format(mode))

    ai0 = [sigma00, alpha0, mfp0]

#=============================
# other functions
#=============================
def savecsv(outfile, header, datalist):
    try:
        print("Write to [{}]".format(outfile))
        f = open(outfile, 'w')
    except:
# except IOError:
        print("Error: Can not write to [{}]".format(outfile))
    else:
        fout = csv.writer(f, lineterminator='\n')
        fout.writerow(header)
# fout.writerows(data)
        for i in range(0, len(datalist[0])):
            a = []
            for j in range(len(datalist)):
                a.append(datalist[j][i])
            fout.writerow(a)
        f.close()

def read_csv(infile, xmin = None, xmax = None, delimiter = ','):
    data = []
    try:
        infp = open(infile, "r")
        f = csv.reader(infp, delimiter = delimiter)
        header = next(f)
        print("header=", header)
        for j in range(len(header)):
            data.append([])

        for row in f:
# print("row=", row)
            x = pfloat(row[0])
            if xmin is not None and xmin <= x <= xmax:
                y = pfloat(row[1])
                data[0].append(x)
                data[1].append(y)
    except:
        terminate("Error: Can not read [{}]".format(infile))

    return header, data[0], data[1]


def recover_parameters(ais, optid):
    global ai0, sigma00, alpha0, mfp0

    ai = []
    c = 0
    for i in range(len(optid)):
        if optid[i] == 1:
            ai.append(ais[c])
            c += 1
        else:
            ai.append(ai0[i])
    return ai

def choose_parameters(ais, optid):
    global ai0, sigma00, alpha0, mfp0

    ai = []
    c = 0
    for i in range(len(optid)):
        if optid[i] == 1:
            ai.append(ais[i])
    return ai

# 弱局在による磁気伝導率変化 Δσ(B)の計算
def sigma_WL(B, l_in, alpha):
    x = l_in * l_in * 4.0 * e * abs(B) / hbar
    dsigma = scipy.special.digamma(0.5 + 1.0 / x) + log(x) #縦軸 e*e/pi/hbar単位
    return alpha * kMR * dsigma

def sigma(B, l_in, alpha, sigma0):
    return sigma0 + sigma_WL(B, l_in, alpha)

def calS2(xk):
    global model
    global Bxx, MRxx, MCxx
    global alpha0

    sigma0, alpha, mfp = recover_parameters(xk, optid)
    sigma0 = max([0.0, sigma0])
    mfp = max([0.0, mfp])
    l_in = mfp * nm2m

    S2 = 0.0
    for i in range(len(Bxx)):
        B = Bxx[i]
        if abs(B) < Bth:
            continue

        sigmacal = sigma(B, l_in, alpha0, sigma0)
        S2 += pow(sigmacal - MCxx[i], 2)
    return S2 / len(Bxx)

# First derivatives to be used e.g. for cg method
# Approximate by forward difference method with the delta h = h_diff
def diff1(ai):
    n = len(ai)
    f0 = calS2(ai)
    df = np.empty(n)
    for i in range(n):
        aii = ai
        aii[i] = ai[i] + h_diff
        df[i] = (calS2(aii) - f0) / h_diff
    return df

# Callback function for scipy.optimize.minimize()
# Print variables every iteration, and update graph for every graphupdateinterval iterationsおt
iter = 0
def callback(fig, xk):
    global iter

    S2 = calS2(xk)
    sigma0, alpha, mfp = recover_parameters(xk, optid)

# パラメータと残差二乗和を表示
    print("callback {}: sigma0={:14.4g} alpha={:10.4g} mfp={:10.4g} S2={:12.6g}".format(iter, sigma0, alpha, mfp, S2))
    iter += 1


def fitting():
    global file_xx
    global mfp0, alpha0, sigma00
    global Bxxmin, Bxxmax
    global headerxx, Bxx, MRxx, MCxx
    global fig

    print("")
    print("MR_xx file : ", file_xx)
    print(" Read [{}]".format(file_xx))
    headerxx, Bxx, MRxx = read_csv(file_xx, Bxxmin, Bxxmax)
    print(" Bxx range: {} - {}".format(Bxxmin, Bxxmax))
    print(" thickness: {} nm".format(thickness))
    print("")
    print("Parameters")
    print(" sigma0: {} S".format(sigma00))
    print(" alpha: {}".format(alpha0))
    print(" mfp: {} nm".format(mfp0))
    print("")
    print("Optimization configuration")
    print(" method: ", method)
    print(" maxiter: ", maxiter)
    print(" tol : ", tol)
    print("")

    MCxx = []
    for i in range(len(Bxx)):
        MCxx.append(1.0 / MRxx[i] * thickness * nm2m) # S/m * thickness[m]

    print("")
    print("Calculate initial data")
    l_in = mfp0 * nm2m
    yMCxxcal = []
    print("{:4s}\t{:8s}\t{:10s}\t{:10s}".format('i', 'B(T)', 'Ssheet(exp)(S)', 'Ssheet(cal)(S)'))
    for i in range(len(Bxx)):
        B = Bxx[i]
        if abs(B) < Bth:
            continue

        sigmacal = sigma(B, l_in, alpha0, sigma00)
        print("{:4d}\t{:8.4f}\t{:10.4g}\t{:10.4g}".format(i, B, MCxx[i], sigmacal))
        yMCxxcal.append(sigmacal)

#=============================
# Optimization
#=============================
    print("")
    print("Optimization by {}".format(method))
    print(" tol=", tol)
    print(" ai0 =", ai0)
    print(" optid=", optid)
    ai = choose_parameters(ai0, optid)
    print(" ai =", ai)

    ret = minimize(calS2, ai, method = method, jac = diff1, tol = tol,
                callback = lambda xk: callback(fig, xk),
                options = {'maxiter':maxiter, "disp":True})
    print("ret=", ret)

    if method == 'nelder-mead':
        simplex = ret['final_simplex']
        ai = simplex[0][0]
        fmin = ret['fun']
    elif method == 'cg':
        ai = ret['x']
        fmin = ret['fun']
    elif method == 'powell':
        ai = ret['x']
        fmin = ret['fun']
    elif method == 'bfgs':
        ai = ret['x']
        fmin = calS2(ai)

    sigma0, alpha, mfp = recover_parameters(ai, optid)
    print("Optimized at S2={:12.6g}:".format(fmin))
    print(" sigma0={:12.6g} S".format(sigma0))
    print(" alpha={:12.6g}".format(alpha))
    print(" mfp={:12.6g} nm".format(mfp))

    print("")
    print("Calculate final data")
    l_in = mfp * nm2m
    yMCxxfit = []
    print("sigma0: {} S".format(sigma0))
    print("{:4s}\t{:8s}\t{:10s}\t{:10s}".format('i', 'B(T)', 'dSsheet(exp)(S)', 'dSsheet(cal)(S)'))
    for i in range(len(Bxx)):
        B = Bxx[i]
        if abs(B) < Bth:
            continue

        sigmafit = sigma(B, l_in, alpha, sigma0)
        print("{:4d}\t{:8.4f}\t{:10.4g}\t{:10.4g}".format(i, B, MCxx[i] - sigma0, sigmafit - sigma0))
        yMCxxfit.append(sigmafit)

#=============================
# Save fitting results
#=============================
    print("")
    print("Save fitting results to [{}]".format(output_csv))
    savecsv(output_csv, ["B(T)", "rhoxx(exp)(ohm*m)", "Ssheet(exp)(S)", "Ssheet(ini)(S)", "Ssheet(fin)(S)"],
            [Bxx, MRxx, MCxx, yMCxxcal, yMCxxfit])

    print("")
    print("Save fitting results to [{}]".format(output_txt))
    f = open(output_txt, 'w')
    f.write("file_xx={}\n".format(file_xx))
    f.write("file_xy={}\n".format(file_xy))
    f.write("thickness={} nm\n".format(thickness))
    f.write("Bxxmin={} T\n".format(Bxxmin))
    f.write("Bxxmax={} T\n".format(Bxxmax))
    f.write("sigma0={} S\n".format(sigma0))
    f.write("alpha={}\n".format(alpha))
    f.write("mfp={} nm\n".format(mfp))
    f.close()


#=============================
# Plot graphs
#=============================
    print("")
    print("plot")
    fig = plt.figure(figsize = (8, 8))
    ax1 = fig.add_subplot(2, 2, 1)
    ax2 = fig.add_subplot(2, 2, 3)
    ax3 = fig.add_subplot(2, 2, 4)

    ax1.plot(Bxx, MRxx, label = 'MRxx', linestyle = 'none', marker = 'o', markersize = 1.0)
# ax1.plot(Bxx, yds, label = 'MRxx(WL,cal)', color = 'red')
    ax1.set_xlabel("B(T)")
    ax1.set_ylabel("rhoxx(ohm*m)")
    ax1.set_title(file_xx)
    ax1.legend()
    ax2.plot(Bxx, MCxx, label = 'exp', linestyle = 'none', marker = 'o', markersize = 1.0)
    ax2.plot(Bxx, yMCxxcal, label = 'initial', linestyle = '-', color = 'red', linewidth = 1.0)
    ax2.plot(Bxx, yMCxxfit, label = 'final', linestyle = '-', color = 'blue', linewidth = 1.0)
    ax2.set_xlabel("B(T)")
    ax2.set_ylabel("Ssheet,xx(S)")
    ax2.set_title(file_xx)
    ax2.legend()
    ax3.plot(Bxx, yMCxxfit, label = 'Ssheet,xx(WL,cal)')
    ax3.set_xlabel("B(T)")
    ax3.set_ylabel("Ssheet,xx(S)")
    ax3.set_title("l_in = {:12.6g} nm".format(mfp))
    ax3.legend()
    plt.tight_layout()

    plt.pause(0.1)

    print("")
    usage()
    print("")

    print("Next suggestion:")
    print("python {} fit {} {} {:12.6g} {} {:12.6g} {} {:12.6g} {} {} {}"
            .format(sys.argv[0], file_xx, thickness, sigma0, optid[0], alpha, optid[1], mfp, optid[2], Bxxmin, Bxxmax))
    print("")

    print("Press ENTER to exit>>", end = '')
    input()

    exit()


def simulate():
    global nm2m
    global mfp0, algpha0, sigma00

    print("B range: {} - {} at {} step, {} points".format(Bmin, Bmax, Bstep, nB))

    l_in = mfp0 * nm2m

    xB = []
    xx = []
    yMCxxcal = []
    yMRxxcal = []
    print("{:4s}\t{:8s}\t{:10s}\t{:10s}\t{:10s}".format('i', 'B(T)', 'x', 'sigma(cal)(S)', 'rho(cal)(ohm*m)'))
    for i in range(nB):
        B = Bmin + i * Bstep
        if abs(B) < Bth:
            continue

        x = l_in * l_in * 4.0 * e * B / hbar # dsの計算のためには必要はないが、printとxxリストへの追加に必要
        dsigma = sigma(B, l_in, alpha0, sigma00)
        rho = 1.0 / dsigma * (thickness * nm2m) # to ohm*m
        print("{:4d}\t{:8.4f}\t{:10.4g}\t{:10.4g}\t{:10.4g}".format(i, B, x, dsigma, rho))
        xB.append(B)
        xx.append(x)
        yMCxxcal.append(dsigma)
        yMRxxcal.append(rho)

    print("")
    print("plot")
    fig = plt.figure(figsize = (8, 8))
    ax1 = fig.add_subplot(2, 2, 1)
    ax2 = fig.add_subplot(2, 2, 2)
    ax3 = fig.add_subplot(2, 2, 3)
    ax4 = fig.add_subplot(2, 2, 4)

    plt.title("mfp = {} nm".format(mfp0))
    ax1.plot(xB, yMRxxcal, label = 'rho-B')
    ax1.set_xlabel("B(T)")
    ax1.set_ylabel("Ssheet(S)")
    ax1.legend()
    ax2.plot(xx, yMRxxcal, label = 'rho-x')
# ax2.set_ylim([-10, 10])
    ax2.set_xlabel("x = l_in^2*4*e*B/hbar")
    ax2.set_ylabel("Ssheet(S)")
    ax2.legend()
    ax3.plot(xB, yMCxxcal, label = 'Ssheet-B')
    ax3.set_xlabel("B(T)")
    ax3.set_ylabel("Ssheet(S)")
    ax3.legend()
    ax4.plot(xx, yMCxxcal, label = 'Ssheet-x')
# ax4.set_ylim([-10, 10])
    ax4.set_xlabel("x = l_in^2*4*e*B/hbar")
    ax4.set_ylabel("Ssheet(S)")
    ax4.legend()
    plt.tight_layout()

    plt.pause(0.1)

    print("")
    usage()
    print("")

    print("Press ENTER to exit>>", end = '')
    input()


def main():
    global mode
    global mfp
    global Bmin, Bmax, Bstep, nB

    updatevars()

    print("")
    print("mode: ", mode)
    print("sigma0 : {} S".format(sigma00))
    print("alpha : {} nm".format(alpha0))
    print("Mean free path: {} nm".format(mfp0))

    if mode == 'sim':
        simulate()
    elif mode == 'fit':
        fitting()
    else:
        terminate("Error: Invalide mode [{}]".format(mode))

    terminate()


if __name__ == '__main__':
    main()