1+ ---
2+ title: NAND Gate Differential-Algebraic Equation (DAE) Work-Precision Diagrams
3+ author: Jayant Pranjal
4+ ---
5+
6+ ```julia
7+ using OrdinaryDiffEq, DiffEqDevTools, ModelingToolkit, ODEInterfaceDiffEq,
8+ Plots
9+ using LinearAlgebra
10+ using ModelingToolkit: t_nounits as t, D_nounits as D
11+ ```
12+
13+ ## Problem Parameters
14+
15+ ```julia
16+ const RGS = 4.0
17+ const RGD = 4.0
18+ const RBS = 10.0
19+ const RBD = 10.0
20+ const CGS = 6e-5
21+ const CGD = 6e-5
22+ const CBD = 2.4e-5
23+ const CBS = 2.4e-5
24+ const C9 = 5e-5
25+ const DELTA = 0.02
26+ const CURIS = 1e-14
27+ const VTH = 25.85
28+ const VDD = 5.0
29+ const VBB = -2.5
30+ const VT0_DEPL = -2.43
31+ const CGAMMA_DEPL = 0.2
32+ const PHI_DEPL = 1.28
33+ const BETA_DEPL = 5.35e-4
34+ const VT0_ENH = 0.2
35+ const CGAMMA_ENH = 0.035
36+ const PHI_ENH = 1.01
37+ const BETA_ENH = 1.748e-3
38+ ```
39+
40+ ## Input Signal Functions
41+
42+ ```julia
43+ function pulse(t, t_start, v_low, t_rise, v_high, t_high, t_fall, t_period)
44+ t_mod = mod(t, t_period)
45+
46+ if t_mod < t_start
47+ return v_low
48+ elseif t_mod < t_start + t_rise
49+ return v_low + (v_high - v_low) * (t_mod - t_start) / t_rise
50+ elseif t_mod < t_start + t_rise + t_high
51+ return v_high
52+ elseif t_mod < t_start + t_rise + t_high + t_fall
53+ return v_high - (v_high - v_low) * (t_mod - t_start - t_rise - t_high) / t_fall
54+ else
55+ return v_low
56+ end
57+ end
58+
59+ V1(t) = pulse(t, 0.0, 0.0, 5.0, 5.0, 5.0, 5.0, 20.0)
60+ V2(t) = pulse(t, 0.0, 0.0, 15.0, 5.0, 15.0, 5.0, 40.0)
61+
62+ function V1_derivative(t)
63+ t_mod = mod(t, 20.0)
64+ if 0.0 < t_mod < 5.0
65+ return 1.0
66+ elseif 10.0 < t_mod < 15.0
67+ return -1.0
68+ else
69+ return 0.0
70+ end
71+ end
72+ function V2_derivative(t)
73+ t_mod = mod(t, 40.0)
74+ if 0.0 < t_mod < 15.0
75+ return 1.0/15.0
76+ elseif 20.0 < t_mod < 35.0
77+ return -1.0/15.0
78+ else
79+ return 0.0
80+ end
81+ end
82+ ```
83+
84+ ## MOSFET Model Functions
85+
86+ ```julia
87+ function gdsp(ned, vds, vgs, vbs)
88+ if ned == 1
89+ vt0, cgamma, phi, beta = VT0_DEPL, CGAMMA_DEPL, PHI_DEPL, BETA_DEPL
90+ else
91+ vt0, cgamma, phi, beta = VT0_ENH, CGAMMA_ENH, PHI_ENH, BETA_ENH
92+ end
93+ phi_vbs = max(phi - vbs, 1e-12)
94+ phi_safe = max(phi, 1e-12)
95+ vte = vt0 + cgamma * (sqrt(phi_vbs) - sqrt(phi_safe))
96+ if vgs - vte <= 0.0
97+ return 0.0
98+ elseif 0.0 < vgs - vte <= vds
99+ return -beta * (vgs - vte)^2 * (1.0 + DELTA * vds)
100+ elseif 0.0 < vds < vgs - vte
101+ return -beta * vds * (2.0 * (vgs - vte) - vds) * (1.0 + DELTA * vds)
102+ else
103+ return 0.0
104+ end
105+ end
106+
107+ function gdsm(ned, vds, vgd, vbd)
108+ if ned == 1
109+ vt0, cgamma, phi, beta = VT0_DEPL, CGAMMA_DEPL, PHI_DEPL, BETA_DEPL
110+ else
111+ vt0, cgamma, phi, beta = VT0_ENH, CGAMMA_ENH, PHI_ENH, BETA_ENH
112+ end
113+ phi_vbd = max(phi - vbd, 1e-12)
114+ phi_safe = max(phi, 1e-12)
115+ vte = vt0 + cgamma * (sqrt(phi_vbd) - sqrt(phi_safe))
116+ if vgd - vte <= 0.0
117+ return 0.0
118+ elseif 0.0 < vgd - vte <= -vds
119+ return beta * (vgd - vte)^2 * (1.0 - DELTA * vds)
120+ elseif 0.0 < -vds < vgd - vte
121+ return -beta * vds * (2.0 * (vgd - vte) + vds) * (1.0 - DELTA * vds)
122+ else
123+ return 0.0
124+ end
125+ end
126+
127+ function ids(ned, vds, vgs, vbs, vgd, vbd)
128+ if vds > 0.0
129+ return gdsp(ned, vds, vgs, vbs)
130+ elseif vds == 0.0
131+ return 0.0
132+ else
133+ return gdsm(ned, vds, vgd, vbd)
134+ end
135+ end
136+
137+ function ibs(vbs)
138+ if vbs <= 0.0
139+ return -CURIS * (exp(vbs / VTH) - 1.0)
140+ else
141+ return 0.0
142+ end
143+ end
144+
145+ function ibd(vbd)
146+ if vbd <= 0.0
147+ return -CURIS * (exp(vbd / VTH) - 1.0)
148+ else
149+ return 0.0
150+ end
151+ end
152+ ```
153+
154+ ## Capacitance Matrix Function
155+
156+ ```julia
157+ function capacitance_matrix!(C, y, t)
158+ fill!(C, 0.0)
159+
160+ C[1,1] = CGS
161+ C[2,2] = CGD
162+ C[3,3] = CBS
163+ C[4,4] = CBD
164+ C[5,5] = 0.0
165+ C[6,6] = CGS
166+ C[7,7] = CGD
167+ C[8,8] = CBS
168+ C[9,9] = CBD
169+ C[10,10] = 0.0
170+ C[11,11] = CGS
171+ C[12,12] = CGD
172+ C[13,13] = CBS
173+ C[14,14] = CBD
174+
175+ return C
176+ end
177+ ```
178+
179+ ## DAE System Definition
180+
181+ ```julia
182+ function nand_rhs!(f, y, p, t)
183+ v1 = V1(t)
184+ v2 = V2(t)
185+ v1d = V1_derivative(t)
186+ v2d = V2_derivative(t)
187+
188+ y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12, y13, y14 = y
189+
190+ f[1] = -(y1 - y5) / RGS - ids(1, y2 - y1, y5 - y1, y3 - y5, y5 - y2, y4 - VDD)
191+ f[2] = -(y2 - VDD) / RGD + ids(1, y2 - y1, y5 - y1, y3 - y5, y5 - y2, y4 - VDD)
192+ f[3] = -(y3 - VBB) / RBS + ibs(y3 - y5)
193+ f[4] = -(y4 - VBB) / RBD + ibd(y4 - VDD)
194+ f[5] = -(y5 - y1) / RGS - ibs(y3 - y5) - (y5 - y7) / RGD - ibd(y9 - y5)
195+
196+ f[6] = CGS * v1d - (y6 - y10) / RGS - ids(2, y7 - y6, v1 - y6, y8 - y10, v1 - y7, y9 - y5)
197+ f[7] = CGD * v1d - (y7 - y5) / RGD + ids(2, y7 - y6, v1 - y6, y8 - y10, v1 - y7, y9 - y5)
198+ f[8] = -(y8 - VBB) / RBS + ibs(y8 - y10)
199+ f[9] = -(y9 - VBB) / RBD + ibd(y9 - y5)
200+ f[10] = -(y10 - y6) / RGS - ibs(y8 - y10) - (y10 - y12) / RGD - ibd(y14 - y10)
201+
202+ f[11] = CGS * v2d - y11 / RGS - ids(2, y12 - y11, v2 - y11, y13, v2 - y12, y14 - y10)
203+ f[12] = CGD * v2d - (y12 - y10) / RGD + ids(2, y12 - y11, v2 - y11, y13, v2 - y12, y14 - y10)
204+ f[13] = -(y13 - VBB) / RBS + ibs(y13)
205+ f[14] = -(y14 - VBB) / RBD + ibd(y14 - y10)
206+
207+ return nothing
208+ end
209+
210+ function nand_mass_matrix!(M, y, p, t)
211+ capacitance_matrix!(M, y, t)
212+ return nothing
213+ end
214+
215+ function create_nand_problem()
216+ y0 = [5.0, 5.0, VBB, VBB, 5.0, 3.62385, 5.0, VBB, VBB, 3.62385, 0.0, 3.62385, VBB, VBB]
217+ dy0 = zeros(14)
218+ tspan = (0.0, 80.0)
219+ M = zeros(14, 14)
220+ capacitance_matrix!(M, y0, 0.0)
221+ f = ODEFunction(nand_rhs!, mass_matrix=M)
222+ prob = ODEProblem(f, y0, tspan)
223+ return prob
224+ end
225+ nand_prob = create_nand_problem()
226+ ```
227+
228+ ## Generate Reference Solution
229+
230+ ```julia
231+ ref_sol = solve(nand_prob, Rodas5P(), abstol=1e-12, reltol=1e-12,
232+ tstops=0.0:5.0:80.0) # Include discontinuity points
233+
234+ plot(ref_sol, title="NAND Gate Circuit - Node Potentials",
235+ xlabel="Time", ylabel="Voltage (V)", legend=:outertopright)
236+ ```
237+
238+ ## Work-Precision Benchmarks
239+
240+ ### High Tolerances
241+
242+ ```julia
243+ abstols = 1.0 ./ 10.0 .^ (4:7)
244+ reltols = 1.0 ./ 10.0 .^ (1:4)
245+
246+ setups = [
247+ Dict(:alg=>Rosenbrock23()),
248+ Dict(:alg=>Rodas4()),
249+ Dict(:alg=>Rodas5P()),
250+ Dict(:alg=>FBDF()),
251+ Dict(:alg=>QNDF()),
252+
253+
254+ ]
255+
256+ wp = WorkPrecisionSet([nand_prob], abstols, reltols, setups;
257+ save_everystep=false, appxsol=[ref_sol],
258+ maxiters=Int(1e6), numruns=5,
259+ tstops=0.0:5.0:80.0)
260+ plot(wp, title="NAND Gate DAE - Work-Precision (High Tolerances)")
261+ ```
262+
263+ ### Medium Tolerances
264+
265+ ```julia
266+ abstols = 1.0 ./ 10.0 .^ (6:9)
267+ reltols = 1.0 ./ 10.0 .^ (3:6)
268+
269+ setups = [
270+ Dict(:alg=>Rodas4()),
271+ Dict(:alg=>Rodas5P()),
272+ Dict(:alg=>FBDF()),
273+ Dict(:alg=>QNDF()),
274+
275+
276+ ]
277+
278+ wp = WorkPrecisionSet([nand_prob], abstols, reltols, setups;
279+ save_everystep=false, appxsol=[ref_sol],
280+ maxiters=Int(1e6), numruns=5,
281+ tstops=0.0:5.0:80.0)
282+ plot(wp, title="NAND Gate DAE - Work-Precision (Medium Tolerances)")
283+ ```
284+
285+ ### Low Tolerances (High Accuracy)
286+
287+ ```julia
288+ abstols = 1.0 ./ 10.0 .^ (8:11)
289+ reltols = 1.0 ./ 10.0 .^ (5:8)
290+
291+ setups = [
292+ Dict(:alg=>Rodas5P()),
293+ Dict(:alg=>FBDF()),
294+ Dict(:alg=>QNDF()),
295+
296+
297+ ]
298+
299+ wp = WorkPrecisionSet([nand_prob], abstols, reltols, setups;
300+ save_everystep=false, appxsol=[ref_sol],
301+ maxiters=Int(1e6), numruns=5,
302+ tstops=0.0:5.0:80.0)
303+ plot(wp, title="NAND Gate DAE - Work-Precision (Low Tolerances)")
304+ ```
305+
306+ ### Timeseries Error Analysis
307+
308+ ```julia
309+ abstols = 1.0 ./ 10.0 .^ (5:8)
310+ reltols = 1.0 ./ 10.0 .^ (2:5)
311+
312+ setups = [
313+ Dict(:alg=>Rodas4()),
314+ Dict(:alg=>Rodas5P()),
315+ Dict(:alg=>FBDF()),
316+
317+
318+ ]
319+
320+ wp = WorkPrecisionSet([nand_prob], abstols, reltols, setups;
321+ error_estimate=:l2, save_everystep=false,
322+ appxsol=[ref_sol], maxiters=Int(1e6), numruns=5,
323+ tstops=0.0:5.0:80.0)
324+ plot(wp, title="NAND Gate DAE - Timeseries Error Analysis")
325+ ```
326+
327+ ## Analysis of Key Nodes
328+
329+ ```julia
330+ key_nodes = [1, 5, 6, 10, 11, 12] # Representative nodes from different parts of the circuit
331+ node_names = ["Node 1", "Node 5", "Node 6", "Node 10", "Node 11", "Node 12"]
332+
333+ p_nodes = plot()
334+ for (i, node) in enumerate(key_nodes)
335+ plot!(ref_sol.t, [u[node] for u in ref_sol.u],
336+ label=node_names[i], linewidth=2)
337+ end
338+ plot!(p_nodes, title="NAND Gate - Key Node Potentials",
339+ xlabel="Time (s)", ylabel="Voltage (V)", legend=:outertopright)
340+ ```
341+
342+ ### Conclusion
343+
344+ ```julia, echo = false
345+ using SciMLBenchmarks
346+ SciMLBenchmarks.bench_footer(WEAVE_ARGS[:folder],WEAVE_ARGS[:file])
347+ ```
0 commit comments