Source code for memtorch.bh.memristor.Stanford_PKU

import math

import numpy as np
import torch

import memtorch
from memtorch.utils import clip, convert_range

from .Memristor import Memristor as Memristor


[docs]class Stanford_PKU(Memristor): """Stanford PKU memristor model (https://nano.stanford.edu/stanford-rram-model). Parameters ---------- time_series_resolution : float Time series resolution (s). r_off : float Off (maximum) resistance of the device (ohms). r_on : float On (minimum) resistance of the device (ohms). gap_init : float Initial gap distance (m). g_0 : float g_0 model parameter. V_0 : float V_0 model parameter. I_0 : float I_0 model parameter. read_voltage : float Read voltage (V) to determine the device's conductance. T_init : float Initial room tempurature. R_th : float Thermal resistance. gamma_init : float gamma_init model parameter. beta : float beta model parameter. t_ox : float Oxide thickness (m). F_min : float Minimum field requirement to enhance gap formation. vel_0 : float vel_0 model parameter. E_a : float Activation energy. a_0 : float Atom spacing. delta_g_init : float Initial delta_g value. model_switch : int Switch to select standard model (0) or dynamic model (1). T_crit : float Threshold temperature (K) for significant random variations. T_smth : float Activation energy for vacancy generation. """ def __init__( self, time_series_resolution=1e-4, r_off=218586, r_on=542, gap_init=2e-10, g_0=0.25e-9, V_0=0.25, I_0=1000e-6, read_voltage=0.1, T_init=298, R_th=2.1e3, gamma_init=16, beta=0.8, t_ox=12e-9, F_min=1.4e9, vel_0=10, E_a=0.6, a_0=0.25e-9, delta_g_init=0.02, model_switch=0, T_crit=450, T_smth=500, **kwargs ): args = memtorch.bh.unpack_parameters(locals()) super(Stanford_PKU, self).__init__( args.r_off, args.r_on, args.time_series_resolution, 0, 0 ) self.gap_init = args.gap_init self.g_0 = args.g_0 self.V_0 = args.V_0 self.I_0 = args.I_0 self.read_voltage = args.read_voltage self.T_init = args.T_init self.R_th = args.R_th self.gamma_init = args.gamma_init self.beta = args.beta self.t_ox = args.t_ox self.F_min = args.F_min self.vel_0 = args.vel_0 self.E_a = args.E_a self.a_0 = args.a_0 self.delta_g_init = args.delta_g_init self.model_switch = args.model_switch self.T_crit = args.T_crit self.T_smth = args.T_smth gap_min = self.g_0 * np.log( self.I_0 * np.sinh(self.read_voltage / self.V_0) / (self.read_voltage / self.r_on) ) gap_max = self.g_0 * np.log( self.I_0 * np.sinh(self.read_voltage / self.V_0) / (self.read_voltage / self.r_off) ) assert ( gap_min > 0 and gap_max > 0 and gap_max > gap_min ), "Invalid gap length bounds (min: %f, max: %f) encountered." % ( gap_min, gap_max, ) self.gap_min = gap_min self.gap_max = gap_max self.gap = max(min(gap_init, gap_max), gap_min) self.g = self.current(self.read_voltage) / self.read_voltage
[docs] def current(self, voltage): """Method to determine the current of the model given an applied voltage. Parameters ---------- voltage : float The current applied voltage (V). Returns ------- float The observed current (A). """ return self.I_0 * np.exp(-self.gap / self.g_0) * np.sinh(voltage / self.V_0)
[docs] def T_current(self, voltage, current): """Method to determine the thermal current of the model given an applied voltage and current. Parameters ---------- voltage : float The current applied voltage (V). current : float The current applied current (A). Returns ------- float The observed thermal current (A). """ return self.T_init + abs(voltage * current * self.R_th)
[docs] def dg_dt(self, voltage, current): """Method to determine the derivative of the gap length. Parameters ---------- voltage : float The current applied voltage (V). current : float The current applied current (A). Returns ------- float The derivative of the gap length. """ q = 1.6e-19 k_b = 1.3806503e-23 gamma = self.gamma_init - self.beta * np.power(self.gap / 1e-9, 3) if gamma * abs(voltage) / self.t_ox < self.F_min: gamma = 0 delta_g = self.delta_g_init * self.model_switch T_current_eval = self.T_current(voltage, current) return -self.vel_0 * np.exp(-q * self.E_a / k_b / T_current_eval) * np.sinh( gamma * self.a_0 / self.t_ox * q * voltage / k_b / T_current_eval ) + np.random.normal(loc=0, scale=1) * delta_g / ( 1 + np.exp((self.T_crit - T_current_eval) / self.T_smth) )
[docs] def simulate(self, voltage_signal, return_current=False): len_voltage_signal = 1 try: len_voltage_signal = len(voltage_signal) except: voltage_signal = [voltage_signal] if return_current: current = np.zeros(len_voltage_signal) np.seterr(all="raise") current_ = 0 for t in range(0, len_voltage_signal): self.gap = ( self.gap + self.dg_dt(voltage_signal[t], current_) * self.time_series_resolution ) self.gap = max(min(self.gap, self.gap_max), self.gap_min) if voltage_signal[t] != 0: self.g = current_ / voltage_signal[t] current_ = self.current(voltage_signal[t]) if return_current: current[t] = current_ if return_current: return current
[docs] def set_conductance(self, conductance): conductance = clip(conductance, 1 / self.r_off, 1 / self.r_on) gap = self.g_0 * np.log( self.I_0 * np.sinh(self.read_voltage / self.V_0) / (self.read_voltage / (1 / conductance)) ) self.gap = max(min(gap, self.gap_max), self.gap_min)
[docs] def plot_hysteresis_loop( self, duration=0.5, voltage_signal_amplitude=1.5, voltage_signal_frequency=10, log_scale=False, return_result=False, ): return super().plot_hysteresis_loop( self, duration=duration, voltage_signal_amplitude=voltage_signal_amplitude, voltage_signal_frequency=voltage_signal_frequency, log_scale=log_scale, return_result=return_result, )
[docs] def plot_bipolar_switching_behaviour( self, voltage_signal_amplitude=1.5, voltage_signal_frequency=0.05, log_scale=True, return_result=False, ): return super().plot_bipolar_switching_behaviour( self, voltage_signal_amplitude=voltage_signal_amplitude, voltage_signal_frequency=voltage_signal_frequency, log_scale=log_scale, return_result=return_result, )