import math
import numpy as np
import memtorch
from memtorch.utils import clip
from .Memristor import Memristor as Memristor
[docs]class Data_Driven2021(Memristor):
"""An updated Data-Driven Verilog-A ReRAM Model. Based on the model used in the article at the following address:
https://arxiv.org/abs/2012.02267. The main difference with the 2018 version (simply Data_Driven.py) is that it is
less computationally demanding and achieves similar results. The creator of the data driven model as decided to
return to the 2017 version for this reason in his recent works.
The default parameters were determined experimentally.
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).
A_p : float
A_p model parameter.
A_n : float
A_n model parameter.
t_p : float
t_p model parameter.
t_n : float
t_n model parameter.
k_p : float
k_p model parameter.
k_n : float
k_n model parameter.
r_p : float
r_p voltage-dependent resistive boundary function coefficients.
r_n : float
r_n voltage-dependent resistive boundary function coefficients.
a_p : float
a_p model parameter.
a_n : float
a_n model parameter.
b_p : float
b_p model parameter.
b_n : float
b_n model parameter.
"""
def __init__(
self,
time_series_resolution=1e-10,
r_off=3000,
r_on=1600,
A_p=600.10075,
A_n=-34.5988399,
t_p=-0.0212028,
t_n=-0.05343997,
k_p=5.11e-4,
k_n=1.17e-3,
r_p=[2699.2336, -672.930205], # a0_p, a1_p
r_n=[649.413746, -1474.32358], # a0_n, a1_n
a_p=0.32046175,
a_n=0.32046175,
b_p=2.71689828,
b_n=2.71689828,
**kwargs
):
args = memtorch.bh.unpack_parameters(locals())
super(Data_Driven2021, self).__init__(
args.r_off, args.r_on, args.time_series_resolution, 0, 0
)
self.A_p = args.A_p
self.A_n = args.A_n
self.t_p = args.t_p
self.t_n = args.t_n
self.k_p = args.k_p
self.k_n = args.k_n
self.r_p = args.r_p
self.r_n = args.r_n
self.a_p = args.a_p
self.a_n = args.a_n
self.b_p = args.b_p
self.b_n = args.b_n
self.g = 1 / args.r_on
[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")
for t in range(0, len_voltage_signal):
current_ = self.current(voltage_signal[t])
self.g = 1 / self.resistance(voltage_signal[t])
if return_current:
current[t] = current_
if return_current:
return current
[docs] def r_pn(self, voltage, a0, a1):
"""Function to return rp(v) or rn(v)
From the 2017/2021 paper model calculations
Parameters
----------
voltage : float
The current applied voltage (V).
a0, a1: float
The value of a0 and a1
Returns
-------
float
The rp or rn resistance (Ω).
"""
return a0 + a1 * voltage
[docs] def s_pn(self, voltage, A, t):
"""Function to return sp(v) or sn(v)
From the 2017/2021 paper model calculations
Parameters
----------
voltage : float
The current applied voltage (V).
A, t: float
The value of model params A (A_p or A_n) or t(t_p or t_n)
Returns
-------
float
The sp or sn variability.
"""
return A * (math.exp(abs(voltage) / t) - 1)
[docs] def dRdt(self, voltage):
"""Function to return dR/dT
From the 2017/2021 paper model calculations
Parameters
----------
voltage : float
The current applied voltage (V).
a0, a1: float
The value of a0 or a1
Returns
-------
float
The derivative with respect to time of the resistance
"""
R = 1 / self.g
if voltage > 0:
r_p = self.r_pn(voltage, self.r_p[0], self.r_p[1])
s_p = self.s_pn(voltage, self.A_p, self.t_p)
return s_p * (r_p - R) ** 2
if voltage <= 0:
r_n = self.r_pn(voltage, self.r_n[0], self.r_n[1])
s_n = self.s_pn(voltage, self.A_n, self.t_n)
return s_n * (R - r_n) ** 2
return
[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).
"""
if voltage > 0:
return self.a_p * self.g * math.sinh(self.b_p * voltage)
else:
return self.a_n * self.g * math.sinh(self.b_n * voltage)
[docs] def resistance(self, voltage):
"""Method to determine the resistance of the model given an applied voltage.
Using the 2017/2021 model
Parameters
----------
voltage : float
The current applied voltage (V).
Returns
-------
float
The observed resistance (Ω).
"""
R0 = 1 / self.g
if voltage > 0:
r_p = self.r_pn(voltage, self.r_p[0], self.r_p[1])
s_p = self.s_pn(voltage, self.A_p, self.t_p)
resistance_ = (
R0 + (s_p * r_p * (r_p - R0)) * self.time_series_resolution
) / (1 + s_p * (r_p - R0) * self.time_series_resolution)
if resistance_ < r_p:
return R0
else:
return max(
min(resistance_, self.r_off), self.r_on
) # Artificially confine the resistance between r_on and r_off
elif voltage < 0:
r_n = self.r_pn(voltage, self.r_n[0], self.r_n[1])
s_n = self.s_pn(voltage, self.A_n, self.t_n)
resistance_ = (
R0 + (s_n * r_n * (r_n - R0)) * self.time_series_resolution
) / (1 + s_n * (r_n - R0) * self.time_series_resolution)
if resistance_ > r_n:
return R0
else:
return max(
min(resistance_, self.r_off), self.r_on
) # Artificially confine the resistance between r_on and r_off
else:
return 1 / self.g
[docs] def set_conductance(self, conductance):
conductance = clip(conductance, 1 / self.r_off, 1 / self.r_on)
self.g = conductance
[docs] def plot_hysteresis_loop(
self,
duration=1e-3,
voltage_signal_amplitude=1.5,
voltage_signal_frequency=10e3,
return_result=False,
):
return super().plot_hysteresis_loop(
self,
duration=duration,
voltage_signal_amplitude=voltage_signal_amplitude,
voltage_signal_frequency=voltage_signal_frequency,
return_result=return_result,
)
[docs] def plot_bipolar_switching_behaviour(
self,
voltage_signal_amplitude=1.5,
voltage_signal_frequency=10e3,
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,
)