🌊

LabRAD入門④:実際の測定コードの例

2021/10/09に公開

ゲート制御可能な2次元電子系の低温測定では基本の測定である、磁場とゲート電圧を変調させて電気抵抗を測定するという測定コードをコメント付きで載せておきます。(下記コードを実際に実験を行っていたときに使っていたコードの一部なのでPython2で書かれており、かつ一部コメントが英語になっています。)ほぼ完全版はこちら、またラボで使われていたコード例はこちら

使用機器は

  1. SR860 (ロックインアンプ)
  2. SIM900 + SIM928 (直流電圧)
  3. AMI430 (磁場印加用Power Supply)
import labrad
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import scipy.io as sio
from matplotlib.ticker import ScalarFormatter
import datetime
import time
import math
import os
import inspect
import sys

sns.set('talk', 'whitegrid', 'dark', font_scale=1.2,
        rc={"lines.linewidth": 2, 'grid.linestyle': '--'})

cxn = labrad.connect()
cxn_2 = labrad.connect()
cxn_3 = labrad.connect()

#時定数のパラメーター
tcdel = 3.0

ramp_step = 1000
ramp_delay = 2500

# Initialization
sim = cxn.sim900
sim.select_device()

#ロックインアンプを3台使うので明示的に各GPIB番号を指定
LA1 = cxn.sr860
LA1.select_device('pc_name GPIB Bus - GPIB0::1::INSTR') 
LA2 = cxn_2.sr860
LA2.select_device('pc_name GPIB Bus - GPIB0::2::INSTR')
LA3 = cxn_3.sr860
LA3.select_device('pc_name GPIB Bus - GPIB0::3::INSTR') 

time_constant_1 = LA1.time_constant()
sensitivity_1 = LA1.sensitivity()

#COM番号を明示的に指定
MGz = cxn.ami_430
MGz.select_device('pc_name_serial_server - COM1')

####################################################

#関数の引数を返す関数:パラメーターに何を入れたか記録するのに便利
# credit: https://tottoto.net/python3-get-args-of-current-function/
def get_meas_parameters(offset=None):
    parent_frame = inspect.currentframe().f_back
    info = inspect.getargvalues(parent_frame)
    return {key: info.locals[key] for key in info.args[offset:]}

# ロックインの振幅と周波数を設定
def set_lockin_parameters(amplitude, frequency):
    LA1.sine_out_amplitude(amplitude)
    LA1.frequency(frequency)
    time.sleep(1)
    print '\r',"parameters set done",

# データ記録ファイルの生成
def create_file(file_path, scan_name, scan_var, meas_var):
    DV = cxn.data_vault
    try:
        DV.mkdir(file_path)
        DV.cd(file_path)
    except Exception:
        DV.cd(file_path)

    file_name = file_path + '_' + scan_name
    dv_file = DV.new(file_name, scan_var, meas_var)
    print '\r',"new file created, file numer: ", int(dv_file[1][0:5])

    return int(dv_file[1][0:5])

# txtファイルに測定パラメーターなどの情報を書き込み
def write_meas_parameters(DV, file_path, file_number, date, scan_name, meas_parameters, amplitude, sensitivity):
    
    if not os.path.isfile(meas_details_path+file_path+'.txt'):
        with open(meas_details_path+file_path+'.txt', "w") as f:
            f.write("-------------------------------------"+ "\n")
            f.write("file_number: "+ str(file_number) + "\n" + "date: " + str(date) +"\n" + "measurement:" + str(scan_name) + "\n")
            for k, v in sorted(meas_parameters.items()):
                print(k, v)
                f.write(str(k) + ": "+ str(v) + "\n")
    else:
        with open(meas_details_path+file_path+'.txt', "a") as f:
            f.write("-------------------------------------"+ "\n")
            f.write("file_number: "+ str(file_number) + "\n" + "date: " + str(date) +"\n" + "measurement:" + str(scan_name) + "\n")
            for k, v in sorted(meas_parameters.items()):
                print(k, v)
                f.write(str(k) + ": "+ str(v) + "\n")
                
            t_mc0, t_p0 = read_T()
            f.write("MXC temperature: "+ str(t_mc0) + "\n" + "probe temperature: "+ str(t_p0) + "\n")
            
            time_constant_1 = LA1.time_constant()
            sensitivity_1 = LA1.sensitivity()
            time_constant_2 = LA2.time_constant()
            sensitivity_2 = LA2.sensitivity()
            time_constant_3 = LA3.time_constant()
            sensitivity_3 = LA3.sensitivity()
            
            f.write("time_constant_1: "+ str(time_constant_1) + "\n" + "time_constant_2: "+ str(time_constant_2) + "\n" + "time_constant_3: "+ str(time_constant_3) + "\n")
            f.write("sensitivity_1: "+ str(sensitivity_1) + "\n" + "sensitivity_2: "+ str(sensitivity_2) + "\n" + "sensitivity_3: "+ str(sensitivity_3) + "\n")

# 測定にかかったトータル時間を書き込み
def write_meas_parameters_end(date1, date2, file_path):
    
    with open(meas_details_path+file_path+'.txt', "a") as f:
        f.write("end date: " + str(date2) + "\n"+ "total time: " + str(date2-date1)+ "\n")

# hdf5ファイル内のコントロール変数と測定変数を出力
def get_variables():
    variables = [DV.variables()[0][i][0] for i in range(len(DV.variables()[0]))] + [DV.variables()[1][i][0] for i in range(len(DV.variables()[1]))]

    return  variables

# グラフを描画
def plot_fig(file_name, file_num, data, cl, xsize, ysize, xaxis, yaxis, xscale, yscale, xname, yname, logy, var, unit):
        
    df = pd.DataFrame(data.T, columns=cl)
    
    fig = plt.figure()
    ax1 = fig.add_subplot(311)
    ax2 = fig.add_subplot(312)
    ax3 = fig.add_subplot(313)
    
    df[yaxis[1]] = abs(df[yaxis[1]])
    
    df.plot(x=xaxis, y=yaxis[0], logy=logy[0], ax=ax1, figsize=(xsize, ysize))
    df.plot(x=xaxis, y=yaxis[1], logy=logy[1], ax=ax2, figsize=(xsize, ysize))
    df.plot(x=xaxis, y=yaxis[2], logy=logy[2], ax=ax3, figsize=(xsize, ysize))

    ax1.set_xlabel(xname)
    ax1.set_ylabel(yname[0])
    ax2.set_xlabel(xname)
    ax2.set_ylabel(yname[1])
    ax3.set_xlabel(xname)
    ax3.set_ylabel(yname[2])
    ax1.set_xlim(xscale[0], xscale[1])
    ax2.set_xlim(xscale[0], xscale[1])
    ax3.set_xlim(xscale[0], xscale[1])
    ax1.set_ylim(yscale[0], yscale[1])
    ax2.set_ylim(yscale[2], yscale[3])
    ax3.set_ylim(yscale[4], yscale[5])
    
    ax1.legend(bbox_to_anchor=(0.85, 1.11), loc='upper left', borderaxespad=0, fontsize=18)
    ax2.legend(bbox_to_anchor=(0.85, 1.11), loc='upper left', borderaxespad=0, fontsize=18)
    ax3.legend(bbox_to_anchor=(0.85, 1.11), loc='upper left', borderaxespad=0, fontsize=18)

    ax1.legend().set_visible(False)
    ax2.legend().set_visible(False)
    ax3.legend().set_visible(False)

    plt.rcParams["font.family"] = "sans-serif"
    plt.rcParams["mathtext.fontset"] = "stixsans"
    fig.patch.set_facecolor('white')
    fig.tight_layout()
    
# sim900の電圧値をある範囲で制御し、その時のロックインアンプの電圧値を出力
def sim_sweep(out_ch, vstart, vend, points, delay):
    vs = [[0] * points]
    vs = np.linspace(vstart, vend, num = points)
    d_tmp = None
    p1, p2, p3 = 0, 0, 0

    for jj in range(points):
        sim.dc_set_voltage(out_ch,float("{0:.4f}".format(vs[jj])))
        time.sleep(delay*0.9)
        try:
        #line_data = [DAC.read_voltage(k) for k in in_dac_ch]
            p1, p2, p3 = LA1.x(), LA2.x(), LA3.x()
            line_data = [p1, p2, p3]
        except:
            line_data = [p1, p2, p3]
            
        if d_tmp:
            d_tmp = np.vstack([d_tmp, line_data])
        else:
            d_tmp = line_data

    return  d_tmp.T

# sim900で電圧値を設定
def set_Vg_nodac(voltage_source, dc_channel_0, start_v, end_v):

    sim_sweep(dc_channel_0, start_v, end_v, 100, 0.02)
    time.sleep(1)
    print '\r',"Voltage reached: ",end_v," V",
        
# sim900の電圧を変化させた場合のロックインアンプの電圧値を出力から電気抵抗などを計算し, numpy arrayとして出力
def scan_Vg(meas_voltage_gain, dc_channel_0, start_v, end_v, number_of_vg_points, wait_time):
    vg = np.linspace(start_v, end_v, number_of_vg_points)   
    
    print '\r','Scanning Vg:  Start_V:', start_v, ' V; End_V:', end_v, ' V',

    res = sim_sweep(dc_channel_0, start_v, end_v, number_of_vg_points, wait_time/4/1e6)

    res_1, res_2, res_3 = res

    # Calculate resistance
    res_5 = np.float64(1.0)*res_2/res_1/meas_voltage_gain
    res_6 = np.float64(1.0)*res_3/res_1/meas_voltage_gain
    
    # Calculate conductance
    res_7 = np.float64(1.0)/res_5
    res_8 = np.float64(1.0)/res_6

    return np.array([res_1, res_2, res_3, res_5, res_6, res_7, res_8])

# AMI420/430という装置で磁場を目標値に設定
def set_Bz(MGz, target_bz):
    MGz.conf_ramp_rate_field(1, 0.0606, 9.0)
    MGz.conf_field_targ(float(target_bz))
    MGz.ramp()
    print '\r',"Ramping magnetic field to ",target_bz," T",
            
    flag = True
    while flag:
        try:
            actual_field = float(MGz.get_field_mag())
            flag = False
        except:
            flag = True

    while abs(float(target_bz)- float(MGz.get_field_mag())) >1.0e-4:
        continue

    time.sleep(1)
    print '\r',"Magnetic field reached: ",target_bz," T",
    return float(MGz.get_field_mag())

# Lakeshoreで温度の読み取り
def read_T():
    reconnected = False
    while not reconnected:
        try:
            time.sleep(1.0)
            print '\r', "Connecting to temperature server ..."
            #cxn4 = labrad.connect('evaporator-PC', 7682, password='pass')
            cxn4 = labrad.connect()
            tc = cxn4.lakeshore_372
            tc.select_device()
            Tmc, T_p = tc.mc(), tc.probe()
            print '\r', 'MXC: ', Tmc, 'probe: ', T_p
            time.sleep(1.0)
            print '\r', 'Reconnected successfully',
            return Tmc, T_p
        except Exception as e:
            print '\r', str(e),
            print '\r', 'Could not reconnect to temperature server',
            time.sleep(2.0)
                    
# Lakeshoreで温度の設定
def set_T(setpoint):
    setpoint_updated = False
    while not setpoint_updated:
        try:
            expression = "SETP 0, %03.2E\n"%setpoint
            #print '\r', expression
            tc.write(expression)  # set the power for the current zone
            Tmc, T_p = tc.mc(), tc.probe()
            print '\r', 'MXC: ', Tmc, 'probe: ', T_p,
            if setpoint < 1.0:
                temperature_error = min(setpoint*0.03, 0.01)
            elif setpoint > 6.5 and setpoint < 10.0:
                temperature_error = 0.3
            elif setpoint > 10.0: temperature_error = 1.0 
            else: temperature_error = 0.1
            

            while abs(Tmc - setpoint) > temperature_error:
                time.sleep(2.0)
                Tmc, T_p = tc.mc(), tc.probe()
                print '\r', 'current MXC: ', Tmc, 'current probe: ', T_p,
                
            print '\r', 'Target temperature reached.',
            return Tmc, T_p
            setpoint_updated = True

        except Exception as e:
            print '\r', str(e),
            print '\r', 'Failed to update setpoint',
            reconnected = False
            while not reconnected:
                try:
                    print '\r', "Connecting to temperature server ...",
                    #cxn4 = labrad.connect('evaporator-PC', 7682, password='pass')
                    cxn4 = labrad.connect()
                    tc = cxn4.lakeshore_372
                    tc.select_device()
                    Tmc, T_p = tc.mc(), tc.probe()
                    print '\r', 'MXC: ', Tmc, 'probe: ', T_p,
                    print '\r', 'Reconnected successfully',
                    reconnected = True
                except Exception as e:
                    print '\r', str(e),
                    print '\r', 'Could not reconnect to temperature server',
                    time.sleep(2.0)
                    
# 電気抵抗(ロックインアンプSR860で測定)をゲート電圧(sim900で制御)と磁場(AMI420/430で制御)に対して測定、データを保存、データをプロット
def scan_R_vs_Vg_Bz(misc, file_path, amplitude, frequency, gate_gain, meas_voltage_gain, voltage_source, voltage_channel,bz_range, vg_range, number_of_bz_lines, number_of_vg_points, wait_time):
    #Get date, parameters and scan name
    cxn0 = labrad.connect()
    DV = cxn0.data_vault
    date1 = datetime.datetime.now()
    meas_parameters = get_meas_parameters()
    scan_name = sys._getframe().f_code.co_name

    #Initial settings of lockins
    set_lockin_parameters(amplitude, frequency)
    
    #Create data file and save measurement parameters
    scan_var = ('Vg_ind', 'Vg', 'Bz_ind', 'Bz', 'Bz_ac', 'Tmc', 'Tp')
    meas_var = ('Ix', 'V1', 'V2', 'R1', 'R2', 'G1', 'G2')
    file_number = create_file(DV, file_path, scan_name, scan_var, meas_var)
    write_meas_parameters(DV, file_path, file_number, date1, scan_name, meas_parameters, amplitude, frequency)

    #Create data points/grids
    b_lines = np.linspace(bz_range[0], bz_range[1], number_of_bz_lines)
    
    t_mc0, t_p0 = read_T()
    ##### Measurements start #####
    # go to initial gate volatge
    set_Vg_nodac(voltage_channel, 0.0, vg_range[0])

    for ind, val in enumerate(b_lines, 1):
        actual_B = set_Bz(MGz, val)
        print '\r',"Field Line:", ind, "out of ", number_of_bz_lines
        
        vg_ind = np.linspace(1, number_of_vg_points, number_of_vg_points)
        vg = gate_gain* np.linspace(vg_range[0], vg_range[1], number_of_vg_points)  
        b_ind = np.linspace(ind, ind, number_of_vg_points)
        b_val = val * np.ones(number_of_vg_points)
        b_ac = actual_B * np.ones(number_of_vg_points)
        t_mc = t_mc0 * np.ones(number_of_vg_points)
        t_p = t_p0 * np.ones(number_of_vg_points)
        
        data1 = np.array([vg_ind, vg, b_ind, b_val, b_ac, t_mc, t_p])
        # Scan Vg and acquire data
        data2 = scan_Vg(meas_voltage_gain, voltage_channel, vg_range[0], vg_range[1], number_of_vg_points, wait_time/4)
        
        data = np.vstack((data1, data2))
        DV.add(data.T)
        
        # if the scan line is 1, plot the line data.
        if number_of_bz_lines == 1:
            plot_fig(file_name = scan_name, file_num = file_number,
                    data = data, cl = list(scan_var) + list(meas_var), 
                    xsize = 12, ysize = 16, xaxis = "Vg", 
                    yaxis = ['Ix', 'R1', 'R2'], xscale = [None, None], 
                    yscale = [None, None, 1, 50000, -1000, 1000], 
                    xname = "Vg", yname = ['Ix', 'R1', 'R2'], 
                    logy = [False, True, False], var = 0, unit = "T")
        
        # go to next gate voltage
        if ind < number_of_bz_lines: 
            set_Vg_nodac(voltage_source, voltage_channel, vg_range[1], vg_range[0])

    # go to 0 V
    set_Vg_nodac(voltage_source, voltage_channel, vg_range[1], 0.0)
    print '\r',"measurement number: ", file_number, scan_name, " done"
    ##### Measurements done #####
    date2 = datetime.datetime.now()
    write_meas_parameters_end(date1, date2, file_path)


def main():
    scan_R_vs_Vg_Bz(misc = 'example', file_path = 'sample1', 
                    amplitude = 1.0, frequency = 17.777, 
                    gate_gain = 1.0, meas_voltage_gain = 1.0, 
                    voltage_channel = 1, bz_range = [0.0, 12.0], 
                    vg_range = [-5.0, 5.0], number_of_bz_lines = 100, 
                    number_of_vg_points = 1000, 
                    wait_time = 1e6*tcdel*time_constant_1)

if __name__ == '__main__':
    main()

<b>参考</b>
https://github.com/labrad/labrad/wiki
https://github.com/labrad/pylabrad/wiki
https://github.com/FeldmanLab/labrad-servers
https://github.com/afylab/afyservers
https://github.com/afylab/LabRAD-Device-Electrical_Measurement-ONline_Software

Discussion