Skip to content

Instantly share code, notes, and snippets.

@mitbailey
Last active January 25, 2023 05:53
Show Gist options
  • Save mitbailey/e0f2146736c09348eba29290c0b28037 to your computer and use it in GitHub Desktop.
Save mitbailey/e0f2146736c09348eba29290c0b28037 to your computer and use it in GitHub Desktop.
Generates graphs showing capacitor charging curves, the effect of varied resistance, overvoltage, and subsequent charge selection precision with clock frequencies.
import matplotlib.pyplot as plt
import numpy as np
import math as m
def align_yaxis(ax1, v1, ax2, v2):
"""adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1"""
_, y1 = ax1.transData.transform((0, v1))
_, y2 = ax2.transData.transform((0, v2))
inv = ax2.transData.inverted()
_, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2))
miny, maxy = ax2.get_ylim()
ax2.set_ylim(miny+dy, maxy+dy)
def deflect(voltage):
return 0.01894072 * (voltage**2.0683391)
def v_from_deflect(deflection):
return 6.80525 * (deflection**0.4834797156810505)
def deflection():
fig = plt.figure(figsize=(8,8))
ax1 = fig.add_subplot(111)
x_s = [0, 25, 50, 75, 100, 125, 150, 175, 200]
y_s = [0, 14.37, 62.83, 145.33, 264.12, 416.40, 602.47, 819.12, 1056.06]
x = np.linspace(0, 200, 200)
y = [deflect(x_v) for x_v in x]
ax1.scatter(x_s, y_s, label='Measured')
ax1.plot(x, y, label='Best-Fit')
fig.suptitle('DM 25CW015-028 Volt Deflection')
ax1.set_title('Single Actuator Deflection at Voltage, Measured and Best-Fit')
ax1.axes.set_ylabel('Deflection (nm)')
ax1.axes.set_xlabel('Voltage (V)')
ax1.legend(loc='center right', bbox_to_anchor=(1,0.35))
fig.savefig('deflection.png', dpi=500)
def c_curve(C, TARGET_V, clk, PRECISION):
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(16,8), sharey=True)
# fig = plt.figure(figsize=(8,8))
# ax1 = fig.add_subplot(112)
ax_b = ax[0].twinx()
TARGET_dV = (TARGET_V) / PRECISION
ax_b.axhline(TARGET_dV, c='k', linestyle=(0,(1,1)), label='Target \u0394Vmax')
ax_b.axhline(TARGET_dV * 2, c='k', linestyle=(0,(1,2)))
ax_b.axhline(TARGET_dV * 4, c='k', linestyle=(0,(1,4)))
ax_b.axhline(TARGET_dV * 8, c='k', linestyle=(0,(1,8)))
V = 150
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
x = x[:len(y)]
dy = np.diff(y)
ax_b.plot(x[:-1], dy, c='tab:blue', linestyle='dashed', label='\u0394V / Clock Cycle')
np.sort(dy)[::-1]
ax[0].plot(x, y, c='tab:blue', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
for i, v in enumerate(y):
if v > v_from_deflect(300):
dv = dy[i]
break
ax_b.axhline(dv, linestyle='dashdot', c='orange', label='1/2 Full Deflection (%.4f \u0394V / Clock Cycle)'%(dv))
ax[1].plot([deflect(y_v) for y_v in y], y, c='tab:orange', label='DM 25CW015-028 Deflection (nm)')
ax[1].set_title('DM 25CW015-028 Deflection')
ax[1].axes.set_ylabel('Charge Proxy (V) [Solid]')
ax[1].axes.set_xlabel('Actuator Deflection (nm)')
ax[1].axvline(300, c='r', label='1/2 Full Deflection (300 nm)')
ax[1].axhline(v_from_deflect(300), c='r', label='1/2 Full Deflection (%.4f V)'%(v_from_deflect(300)))
ax[0].axhline(v_from_deflect(300), c='r', label='1/2 Full Deflection (%.4f V)'%(v_from_deflect(300)))
ax[1].legend()
fig.suptitle('Capacitor Charging Curves (500.1nF, %.2f kHz, Target \u0394Vmax < %.4f V)'%(clk/1000, TARGET_dV))
ax[0].set_title('Standard Capacitor Charging Curve')
ax[0].axes.set_ylabel('Charge Proxy (V) [Solid]')
ax_b.axes.set_ylabel('\u0394Charge Proxy (V) [Dashed, Dotted]')
ax[0].axes.set_xlabel('Time (s)')
ax[0].axhline(TARGET_V, c='k', label='%.0f V'%(TARGET_V))
ax[0].legend(loc='center right', bbox_to_anchor=(1,0.35), prop={'size': 8})
ax_b.legend(loc='center right', prop={'size': 8})
ax_b.set_ylim([0,TARGET_dV*16])
align_yaxis(ax[0], 0, ax_b, 0)
fig.savefig('c_curve.png', dpi=500)
def c_curve_r(C, TARGET_V, clk, PRECISION):
fig = plt.figure(figsize=(15,11))
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
TARGET_dV = (C * TARGET_V) / PRECISION
ax2.axhline(TARGET_dV, c='k', linestyle='dotted', label='Target \u0394Qmax')
ax2.axhline(TARGET_dV * 2, c='k', linestyle='dashed', label='Target \u0394Qmax * 2')
V = 150
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:blue', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:blue', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
V = 150
R = 23750*2
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:red', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:red', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
V = 150
R = 23750*4
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:green', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:green', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
V = 150
R = 23750*8
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:olive', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:olive', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
fig.suptitle('Capacitor Charging Curves (500.1nF, %.2f kHz, Target \u0394Qmax < %e Coulombs)'%(clk/1000, TARGET_dV))
ax1.set_title('Effects of Increasing Resistance, All Else Equal')
ax1.axes.set_ylabel('Charge (Coulombs) [Solid]')
ax2.axes.set_ylabel('\u0394Charge (Coulombs) [Dashed]')
ax1.axes.set_xlabel('Time (s)')
ax1.axhline(C*TARGET_V, c='k', label='%.0f V'%(TARGET_V))
ax1.legend(loc='center right', bbox_to_anchor=(1,0.35))
ax2.legend(loc='center right')
ax2.set_ylim([0,TARGET_dV*16])
align_yaxis(ax1, 0, ax2, 0)
fig.savefig('c_curve_r.png', dpi=500)
def c_curve_v(C, TARGET_V, clk, PRECISION):
fig = plt.figure(figsize=(15,11))
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
TARGET_dV = (C * TARGET_V) / PRECISION
ax2.axhline(TARGET_dV, c='k', linestyle='dotted', label='Target \u0394Qmax')
ax2.axhline(TARGET_dV * 2, c='k', linestyle='dashed', label='Target \u0394Qmax * 2')
V = 150
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:blue', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:blue', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
V = 200
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:red', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:red', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
V = 250
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:green', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:green', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
V = 300
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:olive', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:olive', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
fig.suptitle('Capacitor Charging Curves (500.1nF, %.2f kHz, Target \u0394Qmax < %e Coulombs)'%(clk/1000, TARGET_dV))
ax1.set_title('Effects of Increasing Voltage (Overvoltage), All Else Equal')
ax1.axes.set_ylabel('Charge (Coulombs) [Solid]')
ax2.axes.set_ylabel('\u0394Charge (Coulombs) [Dashed]')
ax1.axes.set_xlabel('Time (s)')
ax1.axhline(C*TARGET_V, c='k', label='%.0f V'%(TARGET_V))
ax1.legend(loc='center right', bbox_to_anchor=(1,0.35))
ax2.legend(loc='center right')
ax2.set_ylim([0,TARGET_dV*16])
align_yaxis(ax1, 0, ax2, 0)
fig.savefig('c_curve_v.png', dpi=500)
def c_curve_clk(C, TARGET_V, clk, PRECISION):
fig = plt.figure(figsize=(15,11))
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
TARGET_dV = (C * TARGET_V) / PRECISION
ax2.axhline(TARGET_dV, c='k', linestyle='dotted', label='Target \u0394Qmax')
ax2.axhline(TARGET_dV * 2, c='k', linestyle='dashed', label='Target \u0394Qmax * 2')
V = 150
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:blue', linestyle='dashed', label='\u0394Q / Clock Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='purple', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
clk *= 10
V = 150
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:red', linestyle='dashed', label='\u0394Q / Clock Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='purple', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
clk *= 2
V = 150
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:green', linestyle='dashed', label='\u0394Q / Clock Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='purple', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
clk *= 2
V = 150
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:olive', linestyle='dashed', label='\u0394Q / Clock Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='purple', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
fig.suptitle('Capacitor Charging Curves (500.1nF, %d V, %d Ohms Target \u0394Qmax < %e Coulombs)'%(V, R, TARGET_dV))
ax1.set_title('Effects of Increasing Clock Speed, All Else Equal')
ax1.axes.set_ylabel('Charge (Coulombs) [Solid]')
ax2.axes.set_ylabel('\u0394Charge (Coulombs) [Dashed]')
ax1.axes.set_xlabel('Time (s)')
ax1.axhline(C*TARGET_V, c='k', label='%.0f V'%(TARGET_V))
ax1.legend(loc='center right', bbox_to_anchor=(1,0.35))
ax2.legend(loc='center right')
ax2.set_ylim([0,TARGET_dV*16])
align_yaxis(ax1, 0, ax2, 0)
fig.savefig('c_curve_clk.png', dpi=500)
def c_curve_r_v(C, TARGET_V, clk, PRECISION):
fig = plt.figure(figsize=(15,11))
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
TARGET_dV = (C * TARGET_V) / PRECISION
ax2.axhline(TARGET_dV, c='k', linestyle='dotted', label='Target \u0394Qmax')
ax2.axhline(TARGET_dV * 2, c='k', linestyle='dashed', label='Target \u0394Qmax * 2')
V = 150
R = 23750
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:blue', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:blue', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
V = 200
R = 36000
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:red', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:red', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
V = 250
R = 55000
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:green', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:green', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
V = 300
R = 71000
Q = C*V
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:olive', linestyle='dashed', label='\u0394Q / Clock Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:olive', label='%.0f V, %.0f kOhm, \u0394Qmax = %e'%(V, R/1000, dy[0]))
fig.suptitle('Capacitor Charging Curves (500.1nF, %.2f kHz, Target \u0394Qmax < %e Coulombs)'%(clk/1000, TARGET_dV))
ax1.set_title('Effects of Increasing Resistance and Voltage')
ax1.axes.set_ylabel('Charge (Coulombs) [Solid]')
ax2.axes.set_ylabel('\u0394Charge (Coulombs) [Dashed]')
ax1.axes.set_xlabel('Time (s)')
ax1.axhline(C*TARGET_V, c='k', label='%.0f V'%(TARGET_V))
ax1.legend(loc='center right', bbox_to_anchor=(1,0.35))
ax2.legend(loc='center right')
ax2.set_ylim([0,TARGET_dV*16])
align_yaxis(ax1, 0, ax2, 0)
fig.savefig('c_curve_r_v.png', dpi=500)
def c_curve_v_r_applied(C, TARGET_V, clk, PRECISION):
fig = plt.figure(figsize=(15,11))
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
TARGET_dV = (TARGET_V) / PRECISION
ax2.axhline(TARGET_dV, c='k', linestyle=(0,(1,4)), label='Target \u0394Vmax')
ax2.axhline(TARGET_dV * 2, c='k', linestyle=(0,(1,8)))
ax2.axhline(TARGET_dV * 4, c='k', linestyle=(0,(1,16)))
ax2.axhline(TARGET_dV * 8, c='k', linestyle=(0,(1,32)))
# Graphed Curve
V = 150
R = 23750
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
MAX_Y_VAL = y[-1]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:blue', linestyle='dashed', label='\u0394V / Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:blue', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f'%(V, R/1000, dy[0]))
# Graphed Curve
V = 160
R = 45000
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:red', linestyle='dashed', label='\u0394V / Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:red', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f'%(V, R/1000, dy[0]))
# Graphed Curve
V = 185
R = 72000
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:green', linestyle='dashed', label='\u0394V / Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:green', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f'%(V, R/1000, dy[0]))
# Graphed Curve
V = 400
R = 257000
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:olive', linestyle='dashed', label='\u0394V / Cycle')
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:olive', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f'%(V, R/1000, dy[0]))
fig.suptitle('Capacitor Charging Curves (500.1nF, Target \u0394Vmax < %.4f V)'%(TARGET_dV))
ax1.set_title('Applied Use of Overvoltage and Resistance to Achieve Higher Temporal Resolution')
ax1.axes.set_ylabel('Charge Proxy (V) [Solid]')
ax2.axes.set_ylabel('\u0394Charge Proxy (V) [Dashed, Dotted]')
ax1.axes.set_xlabel('Time (s)')
ax1.axhline(TARGET_V, c='k', label='%.0f V'%(TARGET_V))
ax1.legend(loc='center right', bbox_to_anchor=(1,0.35))
ax2.legend(loc='center right')
ax2.set_ylim([0,TARGET_dV*16])
align_yaxis(ax1, 0, ax2, 0)
fig.savefig('c_curve_v_r_applied.png', dpi=500)
def c_curve_v_r_clk_applied(C, TARGET_V, clk, PRECISION):
fig = plt.figure(figsize=(15,11))
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
TARGET_dV = (TARGET_V) / PRECISION
ax2.axhline(TARGET_dV, c='k', linestyle=(0,(1,4)), label='Target \u0394Vmax')
ax2.axhline(TARGET_dV * 2, c='k', linestyle=(0,(1,8)))
ax2.axhline(TARGET_dV * 4, c='k', linestyle=(0,(1,16)))
ax2.axhline(TARGET_dV * 8, c='k', linestyle=(0,(1,32)))
# Graphed Curve
V = 150
R = 23750
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
MAX_Y_VAL = y[-1]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:blue', linestyle='dashed', label='\u0394V / Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:blue', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
# for i, v in enumerate(y):
# if v > v_from_deflect(300):
# dv = dy[i]
# dv_minx = x[i]/x[-1]
# break
# ax2.axhline(dv, xmin=dv_minx, linestyle=(0, (5, 10, 1, 5, 1, 5, 1, 10)), c='tab:blue', label='1/2 Full Deflection (%.4f \u0394V / Clock Cycle)'%(dv))
# Graphed Curve
clk *= 10
V = 160
R = 45000
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:red', linestyle='dashed', label='\u0394V / Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:red', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
# for i, v in enumerate(y):
# if v > v_from_deflect(300):
# dv = dy[i]
# dv_minx = x[i]/x[-1]
# break
# ax2.axhline(dv, xmin=dv_minx, linestyle=(0, (5, 10, 1, 5, 1, 5, 1, 10)), c='tab:red', label='1/2 Full Deflection (%.4f \u0394V / Clock Cycle)'%(dv))
# Graphed Curve
clk *= 2
V = 185
R = 72000
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:green', linestyle='dashed', label='\u0394V / Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:green', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
# for i, v in enumerate(y):
# if v > v_from_deflect(300):
# dv = dy[i]
# dv_minx = x[i]/x[-1]
# break
# ax2.axhline(dv, xmin=dv_minx, linestyle=(0, (5, 10, 1, 5, 1, 5, 1, 10)), c='tab:green', label='1/2 Full Deflection (%.4f \u0394V / Clock Cycle)'%(dv))
# Graphed Curve
clk *= 2
V = 400
R = 257000
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:olive', linestyle='dashed', label='\u0394V / Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:olive', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
# for i, v in enumerate(y):
# if v > v_from_deflect(300):
# dv = dy[i]
# dv_minx = x[i]/x[-1]
# break
# ax2.axhline(dv, xmin=dv_minx, linestyle=(0, (5, 10, 1, 5, 1, 5, 1, 10)), c='tab:olive', label='1/2 Full Deflection (%.4f \u0394V / Clock Cycle)'%(dv))
fig.suptitle('Capacitor Charging Curves (500.1nF, Target \u0394Vmax < %.4f V)'%(TARGET_dV))
ax1.set_title('Applied Use of Overvoltage, Resistance, and Clock Speeds to Achieve Higher Temporal Resolution')
ax1.axes.set_ylabel('Charge Proxy (V) [Solid]')
ax2.axes.set_ylabel('\u0394Charge Proxy (V) [Dashed]')
ax1.axes.set_xlabel('Time (s)')
ax1.axhline(TARGET_V, c='k', label='%.0f V'%(TARGET_V))
ax1.axhline(v_from_deflect(600*0.75), c='tab:purple', label='75%% Deflection (%.4f V)'%(v_from_deflect(600*0.75)))
ax1.axhline(v_from_deflect(600*0.5), c='tab:pink', label='50%% Deflection (%.4f V)'%(v_from_deflect(600*0.5)))
ax1.axhline(v_from_deflect(600*0.25), c='thistle', label='25%% Deflection (%.4f V)'%(v_from_deflect(600*0.25)))
ax1.legend(loc='center right', bbox_to_anchor=(1,0.25))
ax2.legend(loc='center right', bbox_to_anchor=(1,0.5))
plt.xlim([0,x[-1]])
ax2.set_ylim([0,TARGET_dV*16])
align_yaxis(ax1, 0, ax2, 0)
fig.savefig('c_curve_v_r_clk_applied.png', dpi=500)
def c_curve_loose_rc(C, TARGET_V, clk, PRECISION, RC5):
fig = plt.figure(figsize=(15,11))
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
TARGET_dV = (TARGET_V) / PRECISION
ax2.axhline(TARGET_dV, c='k', linestyle=(0,(1,2)), label='Target \u0394Vmax')
ax2.axhline(TARGET_dV * 2, c='k', linestyle=(0,(1,8)), label='2x Target \u0394Vmax')
# Graphed Curve
V = 150
R = 200000*2*RC5
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
MAX_X_VAL = x[-1]
MAX_Y_VAL = y[-1]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:blue', linestyle='dashed', label='\u0394V / Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:blue', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
# for i, v in enumerate(y):
# if v > v_from_deflect(300):
# dv = dy[i]
# dv_minx = x[i]/x[-1]
# break
# ax2.axhline(dv, xmin=dv_minx, linestyle=(0, (5, 10, 1, 5, 1, 5, 1, 10)), c='tab:blue', label='1/2 Full Deflection (%.4f \u0394V / Clock Cycle)'%(dv))
# Graphed Curve
# clk *= 10
V = 160
R = 375000*2*RC5
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:red', linestyle='dashed', label='\u0394V / Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:red', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
# for i, v in enumerate(y):
# if v > v_from_deflect(300):
# dv = dy[i]
# dv_minx = x[i]/x[-1]
# break
# ax2.axhline(dv, xmin=dv_minx, linestyle=(0, (5, 10, 1, 5, 1, 5, 1, 10)), c='tab:red', label='1/2 Full Deflection (%.4f \u0394V / Clock Cycle)'%(dv))
# Graphed Curve
# clk *= 2
V = 185
R = 612000*2*RC5
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:green', linestyle='dashed', label='\u0394V / Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:green', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
# for i, v in enumerate(y):
# if v > v_from_deflect(300):
# dv = dy[i]
# dv_minx = x[i]/x[-1]
# break
# ax2.axhline(dv, xmin=dv_minx, linestyle=(0, (5, 10, 1, 5, 1, 5, 1, 10)), c='tab:green', label='1/2 Full Deflection (%.4f \u0394V / Clock Cycle)'%(dv))
# Graphed Curve
# clk *= 2
V = 200
R = 732500*2*RC5
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax2.plot(x[:-1], dy, c='tab:olive', linestyle='dashed', label='\u0394V / Cycle, %.0f Hz'%(clk))
np.sort(dy)[::-1]
ax1.plot(x, y, c='tab:olive', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
# for i, v in enumerate(y):
# if v > v_from_deflect(300):
# dv = dy[i]
# dv_minx = x[i]/x[-1]
# break
# ax2.axhline(dv, xmin=dv_minx, linestyle=(0, (5, 10, 1, 5, 1, 5, 1, 10)), c='tab:olive', label='1/2 Full Deflection (%.4f \u0394V / Clock Cycle)'%(dv))
fig.suptitle('Capacitor Charging Curves (500.1nF, %.2f kHz, Target \u0394Vmax < %.4f V)'%(clk/1000, TARGET_dV))
ax1.set_title('Applied Use of Overvoltage, Resistance, and Longer RC Time to Achieve Higher Temporal Resolution')
ax1.axes.set_ylabel('Charge Proxy (V) [Solid]')
ax2.axes.set_ylabel('\u0394Charge Proxy (V) [Dashed, Dotted]')
ax1.axes.set_xlabel('Time (s)')
ax1.axhline(TARGET_V, c='k', label='%.0f V'%(TARGET_V))
ax1.axhline(v_from_deflect(600*0.75), c='tab:purple', label='75%% Deflection (%.4f V)'%(v_from_deflect(600*0.75)))
ax1.axhline(v_from_deflect(600*0.5), c='tab:pink', label='50%% Deflection (%.4f V)'%(v_from_deflect(600*0.5)))
ax1.axhline(v_from_deflect(600*0.25), c='thistle', label='25%% Deflection (%.4f V)'%(v_from_deflect(600*0.25)))
ax1.legend(loc='center right', bbox_to_anchor=(1,0.25))
ax2.legend(loc='center right', bbox_to_anchor=(1,0.5))
plt.xlim([0,MAX_X_VAL])
ax2.set_ylim([0,TARGET_dV*4])
align_yaxis(ax1, 0, ax2, 0)
fig.savefig('c_curve_loose_rc_%.2f.png'%(RC5), dpi=500)
def c_curve_asic(C, TARGET_V, clk, PRECISION, RC5):
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(14,14))
TARGET_dV = (TARGET_V) / PRECISION
ax[0, 1].axvline(TARGET_dV, c='k', linestyle=(0,(1,2)), label='\u0394V$_{max}$ (%.4f)'%(TARGET_dV))
ax[1, 1].axhline(600/PRECISION, c='k', linestyle=(0,(1,2)), label='\u0394nm$_{max}$ (%.4f)'%(600/PRECISION))
# Graphed Curve
V = 150
R = 20000*20*RC5
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - (m.e**(-time / (R*C))))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
MAX_X_VAL = x[-1]
MAX_Y_VAL = y[-1]
x = x[:len(y)]
dy = np.diff(y)
ax[0, 1].plot(dy, y[:-1], c='tab:blue', linestyle='dashed', label='\u0394V / Cycle, %.0f kHz'%(clk/1000))
np.sort(dy)[::-1]
ax[0, 0].plot(x, y, c='tab:blue', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
ny = []
for i, t in enumerate(x):
ny.append(deflect(y[i]))
ax[1, 0].plot(x, ny, c='tab:blue', label='')
dny = np.diff(ny)
ax[1, 1].plot(x[:-1], dny, c='tab:blue', linestyle='dashed', label='\u0394V / Cycle, %.0f kHz'%(clk/1000))
V = 185
R = 61250*20*RC5
Q = C*V
x = []
y = []
x = np.linspace(0, R*C*5, int(clk*R*C*5))
y = [(C * V * (1 - m.e**(-time / (R*C)))) for time in x]
y_tv = (C * TARGET_V * (1 - (m.e**(-R*C*5 / (R*C))))) # Charge at TARGET_V voltage, aka max charge.
y = [(y_v / y_tv) * TARGET_V for y_v in y]
y = [val for val in y if val <= MAX_Y_VAL]
x = x[:len(y)]
dy = np.diff(y)
ax[0, 1].plot(dy, y[:-1], c='tab:red', linestyle='dashed', label='\u0394V / Cycle, %.0f kHz'%(clk/1000))
np.sort(dy)[::-1]
ax[0, 0].plot(x, y, c='tab:red', linestyle='solid', label='%.0f V, %.0f kOhm, \u0394Vmax = %.4f V'%(V, R/1000, dy[0]))
ny = []
for i, t in enumerate(x):
ny.append(deflect(y[i]))
ax[1, 0].plot(x, ny, c='tab:red', label='')
dny = np.diff(ny)
ax[1, 1].plot(x[:-1], dny, c='tab:red', linestyle='dashed', label='\u0394V / Cycle, %.0f kHz'%(clk/1000))
fig.suptitle('Capacitor Charging Curves (500.1nF, %.2f MHz, Target \u0394Vmax < %.4f V)'%(clk/1000000, TARGET_dV))
ax[0, 0].set_title('Charge vs Time')
ax[0, 0].axes.set_xlabel('Time (s)')
ax[0, 0].axes.set_ylabel('Charge Proxy (V)')
ax[0, 0].legend()
ax[0, 1].set_title('Change in Charge per Period vs Voltage')
ax[0, 1].axes.set_xlabel('Voltage (V)')
ax[0, 1].axes.set_ylabel('Change in Charge Proxy (\u0394V)')
ax[0, 1].legend()
ax[1, 0].set_title('DM Actuator Displacement vs Time')
ax[1, 0].axes.set_xlabel('Time (s)')
ax[1, 0].axes.set_ylabel('Displacement (nm)')
ax[1, 0].legend()
ax[1, 1].set_title('Change in DM Actuator Displacement per Period vs Time')
ax[1, 1].axes.set_xlabel('Time (s)')
ax[1, 1].axes.set_ylabel('Change in Displacement (\u0394nm)')
ax[1, 1].legend()
fig.savefig('c_curve_asic.png', dpi=500)
if __name__ == '__main__':
C = 500.1 * 1e-9
TARGET_V = 150
clk = 50000 # Hz
PRECISION = 2**14
# deflection()
# c_curve(C, TARGET_V, clk, PRECISION)
# c_curve_r(C, TARGET_V, clk, PRECISION)
# c_curve_v(C, TARGET_V, clk, PRECISION)
# # c_curve_r_v(C, TARGET_V, clk, PRECISION)
# c_curve_clk(C, TARGET_V, clk, PRECISION)
# c_curve_v_r_applied(C, TARGET_V, clk, PRECISION)
# c_curve_v_r_clk_applied(C, TARGET_V, clk, PRECISION)
# c_curve_loose_rc(C, TARGET_V, clk, PRECISION, 0.06)
# c_curve_loose_rc(C, TARGET_V, clk, PRECISION, 0.2)
# c_curve_loose_rc(C, TARGET_V, clk, PRECISION, 0.5)
# c_curve_loose_rc(C, TARGET_V, clk, PRECISION, 1)
# c_curve_loose_rc(C, TARGET_V, clk, PRECISION, 2)
c_curve_asic(C, TARGET_V, 1000 * 1000, PRECISION, 0.06)
plt.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment