@@ -5,7 +5,7 @@ author: Jayant Pranjal
55
66```julia
77using OrdinaryDiffEq, DiffEqDevTools, ModelingToolkit, ODEInterfaceDiffEq,
8- Plots
8+ Plots, Sundials, DASSL, DASKR
99using LinearAlgebra
1010using ModelingToolkit: t_nounits as t, D_nounits as D
1111```
@@ -151,31 +151,6 @@ function ibd(vbd)
151151end
152152```
153153
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-
179154## DAE System Definition
180155
181156```julia
@@ -207,121 +182,201 @@ function nand_rhs!(f, y, p, t)
207182 return nothing
208183end
209184
210- function nand_mass_matrix!(M, y, p, t)
211- capacitance_matrix!(M, y, t)
212- return nothing
213- end
185+ # Capacitance matrix (mass matrix)
186+ dirMassMatrix = [
187+ CGS 0 0 0 0 0 0 0 0 0 0 0 0 0
188+ 0 CGD 0 0 0 0 0 0 0 0 0 0 0 0
189+ 0 0 CBS 0 0 0 0 0 0 0 0 0 0 0
190+ 0 0 0 CBD 0 0 0 0 0 0 0 0 0 0
191+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0
192+ 0 0 0 0 0 CGS 0 0 0 0 0 0 0 0
193+ 0 0 0 0 0 0 CGD 0 0 0 0 0 0 0
194+ 0 0 0 0 0 0 0 CBS 0 0 0 0 0 0
195+ 0 0 0 0 0 0 0 0 CBD 0 0 0 0 0
196+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0
197+ 0 0 0 0 0 0 0 0 0 0 CGS 0 0 0
198+ 0 0 0 0 0 0 0 0 0 0 0 CGD 0 0
199+ 0 0 0 0 0 0 0 0 0 0 0 0 CBS 0
200+ 0 0 0 0 0 0 0 0 0 0 0 0 0 CBD
201+ ]
214202
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()
203+ # Initial conditions
204+ y0 = [5.0, 5.0, VBB, VBB, 5.0, 3.62385, 5.0, VBB, VBB, 3.62385, 0.0, 3.62385, VBB, VBB]
205+ tspan = (0.0, 80.0)
206+
207+ # Create the three problem formulations
208+ mmf = ODEFunction(nand_rhs!, mass_matrix=dirMassMatrix)
209+ mmprob = ODEProblem(mmf, y0, tspan)
210+
211+ # ModelingToolkit version via modelingtoolkitize
212+ mtk_sys = modelingtoolkitize(mmprob)
213+ mtk_simplified = structural_simplify(mtk_sys)
214+ u0_mtk = [mtk_simplified.states[i] => mmprob.u0[i] for i in 1:length(mmprob.u0)]
215+ mtkprob = ODEProblem(mtk_simplified, u0_mtk, mmprob.tspan)
216+
217+ # DAEProblem version
218+ du = mmprob.f(mmprob.u0, mmprob.p, 0.0)
219+ du0 = D.(unknowns(mtk_simplified)) .=> du
220+ daeprob = DAEProblem(mtk_simplified, du0, [], tspan)
221+
222+ # Generate reference solutions
223+ ref_sol = solve(mtkprob, Rodas5P(), abstol=1e-12, reltol=1e-12, tstops=0.0:5.0:80.0)
224+ dae_ref_sol = solve(daeprob, IDA(), abstol=1e-10, reltol=1e-10, tstops=0.0:5.0:80.0)
225+ mm_ref_sol = solve(mmprob, Rodas5P(), abstol=1e-12, reltol=1e-12, tstops=0.0:5.0:80.0)
226+
227+ probs = [mtkprob, daeprob, mmprob]
228+ refs = [ref_sol, ref_sol, mm_ref_sol]
226229```
227230
228- ## Generate Reference Solution
231+ ## Generate Reference Solution and Plot
229232
230233```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
234+ plot(ref_sol, title="NAND Gate Circuit - Node Potentials (MTK)",
235+ xlabel="Time", ylabel="Voltage (V)", legend=:outertopright)
236+ ```
233237
234- plot(ref_sol, title="NAND Gate Circuit - Node Potentials",
238+ ```julia
239+ plot(mm_ref_sol, title="NAND Gate Circuit - Node Potentials (Mass Matrix)",
235240 xlabel="Time", ylabel="Voltage (V)", legend=:outertopright)
236241```
237242
243+ ## Omissions
244+
245+ Some solvers are omitted due to performance or stability issues at certain tolerances.
246+
238247## Work-Precision Benchmarks
239248
240249### High Tolerances
241250
242251```julia
243- abstols = 1.0 ./ 10.0 .^ (4:7 )
252+ abstols = 1.0 ./ 10.0 .^ (5:8 )
244253reltols = 1.0 ./ 10.0 .^ (1:4)
245254
246255setups = [
247- Dict(:alg=>Rosenbrock23()),
248- Dict(:alg=>Rodas4()),
249- Dict(:alg=>Rodas5P()),
250- Dict(:alg=>FBDF()),
251- Dict(:alg=>QNDF()),
252-
253-
256+ Dict(:prob_choice => 1, :alg=>Rodas4()),
257+ Dict(:prob_choice => 1, :alg=>FBDF()),
258+ Dict(:prob_choice => 1, :alg=>QNDF()),
259+ Dict(:prob_choice => 1, :alg=>radau()),
260+ Dict(:prob_choice => 1, :alg=>RadauIIA5()),
261+ Dict(:prob_choice => 2, :alg=>DFBDF()),
262+ Dict(:prob_choice => 2, :alg=>IDA()),
263+ Dict(:prob_choice => 2, :alg=>CVODE_BDF())
254264]
255265
256- wp = WorkPrecisionSet([nand_prob] , abstols, reltols, setups;
257- save_everystep=false, appxsol=[ref_sol] ,
258- maxiters=Int(1e6 ), numruns=5 ,
266+ wp = WorkPrecisionSet(probs , abstols, reltols, setups;
267+ save_everystep=false, appxsol=refs ,
268+ maxiters=Int(1e5 ), numruns=10 ,
259269 tstops=0.0:5.0:80.0)
260270plot(wp, title="NAND Gate DAE - Work-Precision (High Tolerances)")
261271```
262272
263- ### Medium Tolerances
264-
265273```julia
266- abstols = 1.0 ./ 10.0 .^ (6:9 )
267- reltols = 1.0 ./ 10.0 .^ (3:6 )
274+ abstols = 1.0 ./ 10.0 .^ (6:8 )
275+ reltols = 1.0 ./ 10.0 .^ (2:4 )
268276
269277setups = [
270- Dict(:alg=>Rodas4()),
271- Dict(:alg=>Rodas5P()),
272- Dict(:alg=>FBDF()),
273- Dict(:alg=>QNDF()),
274-
275-
278+ Dict(:prob_choice => 1, :alg=>Rosenbrock23()),
279+ Dict(:prob_choice => 1, :alg=>Rodas4()),
280+ Dict(:prob_choice => 2, :alg=>IDA()),
281+ Dict(:prob_choice => 3, :alg=>Rodas5P()),
282+ Dict(:prob_choice => 3, :alg=>Rodas4()),
283+ Dict(:prob_choice => 3, :alg=>FBDF()),
284+ Dict(:prob_choice => 2, :alg=>IDA()),
285+ Dict(:prob_choice => 2, :alg=>CVODE_BDF()),
286+ Dict(:prob_choice => 2, :alg=>DASKR.daskr())
276287]
277288
278- wp = WorkPrecisionSet([nand_prob] , abstols, reltols, setups;
279- save_everystep=false, appxsol=[ref_sol] ,
280- maxiters=Int(1e6 ), numruns=5 ,
289+ wp = WorkPrecisionSet(probs , abstols, reltols, setups;
290+ save_everystep=false, appxsol=refs ,
291+ maxiters=Int(1e5 ), numruns=10 ,
281292 tstops=0.0:5.0:80.0)
282293plot(wp, title="NAND Gate DAE - Work-Precision (Medium Tolerances)")
283294```
284295
285- ### Low Tolerances (High Accuracy)
296+ ### Timeseries Errors
286297
287298```julia
288- abstols = 1.0 ./ 10.0 .^ (8:11 )
289- reltols = 1.0 ./ 10.0 .^ (5:8 )
299+ abstols = 1.0 ./ 10.0 .^ (5:8 )
300+ reltols = 1.0 ./ 10.0 .^ (1:4 )
290301
291302setups = [
292- Dict(:alg=>Rodas5P()),
293- Dict(:alg=>FBDF()),
294- Dict(:alg=>QNDF()),
303+ Dict(:prob_choice => 1, :alg=>Rosenbrock23()),
304+ Dict(:prob_choice => 1, :alg=>Rodas4()),
305+ Dict(:prob_choice => 1, :alg=>FBDF()),
306+ Dict(:prob_choice => 1, :alg=>QNDF()),
307+ Dict(:prob_choice => 1, :alg=>radau()),
308+ Dict(:prob_choice => 1, :alg=>RadauIIA5()),
309+ Dict(:prob_choice => 2, :alg=>DFBDF()),
310+ Dict(:prob_choice => 2, :alg=>IDA()),
311+ Dict(:prob_choice => 2, :alg=>CVODE_BDF())
312+ ]
313+
314+ wp = WorkPrecisionSet(probs, abstols, reltols, setups; error_estimate=:l2,
315+ save_everystep=false, appxsol=refs,
316+ maxiters=Int(1e5), numruns=10,
317+ tstops=0.0:5.0:80.0)
318+ plot(wp, title="NAND Gate DAE - Timeseries Errors (High Tolerances)")
319+ ```
295320
321+ ```julia
322+ abstols = 1.0 ./ 10.0 .^ (6:8)
323+ reltols = 1.0 ./ 10.0 .^ (2:4)
296324
325+ setups = [
326+ Dict(:prob_choice => 1, :alg=>Rosenbrock23()),
327+ Dict(:prob_choice => 1, :alg=>Rodas4()),
328+ Dict(:prob_choice => 2, :alg=>IDA()),
329+ Dict(:prob_choice => 3, :alg=>Rodas5P()),
330+ Dict(:prob_choice => 3, :alg=>Rodas4()),
331+ Dict(:prob_choice => 3, :alg=>FBDF()),
332+ Dict(:prob_choice => 2, :alg=>IDA()),
333+ Dict(:prob_choice => 2, :alg=>CVODE_BDF()),
334+ Dict(:prob_choice => 2, :alg=>DASKR.daskr())
297335]
298336
299- wp = WorkPrecisionSet([nand_prob] , abstols, reltols, setups;
300- save_everystep=false, appxsol=[ref_sol] ,
301- maxiters=Int(1e6 ), numruns=5 ,
337+ wp = WorkPrecisionSet(probs , abstols, reltols, setups; error_estimate=:l2,
338+ save_everystep=false, appxsol=refs ,
339+ maxiters=Int(1e5 ), numruns=10 ,
302340 tstops=0.0:5.0:80.0)
303- plot(wp, title="NAND Gate DAE - Work-Precision (Low Tolerances)")
341+ plot(wp, title="NAND Gate DAE - Timeseries Errors (Medium Tolerances)")
304342```
305343
306- ### Timeseries Error Analysis
344+ ### Low Tolerances (High Accuracy)
345+
346+ This measures performance when high accuracy is needed.
307347
308348```julia
309- abstols = 1.0 ./ 10.0 .^ (5:8 )
310- reltols = 1.0 ./ 10.0 .^ (2:5 )
349+ abstols = 1.0 ./ 10.0 .^ (7:12 )
350+ reltols = 1.0 ./ 10.0 .^ (4:9 )
311351
312352setups = [
313- Dict(:alg=>Rodas4()),
314- Dict(:alg=>Rodas5P()),
315- Dict(:alg=>FBDF()),
316-
317-
353+ Dict(:prob_choice => 1, :alg=>Rodas5P()),
354+ Dict(:prob_choice => 3, :alg=>Rodas5P()),
355+ Dict(:prob_choice => 1, :alg=>Rodas4()),
356+ Dict(:prob_choice => 3, :alg=>Rodas4()),
357+ Dict(:prob_choice => 1, :alg=>FBDF()),
358+ Dict(:prob_choice => 1, :alg=>QNDF()),
359+ Dict(:prob_choice => 1, :alg=>radau()),
360+ Dict(:prob_choice => 1, :alg=>RadauIIA5()),
361+ Dict(:prob_choice => 2, :alg=>DFBDF()),
362+ Dict(:prob_choice => 2, :alg=>IDA()),
363+ Dict(:prob_choice => 2, :alg=>CVODE_BDF()),
364+ Dict(:prob_choice => 2, :alg=>DASKR.daskr())
318365]
319366
320- wp = WorkPrecisionSet([nand_prob], abstols, reltols, setups;
321- error_estimate=:l2, save_everystep=false,
322- appxsol=[ref_sol], maxiters=Int(1e6), numruns=5,
367+ wp = WorkPrecisionSet(probs, abstols, reltols, setups;
368+ save_everystep=false, appxsol=refs,
369+ maxiters=Int(1e5), numruns=10,
370+ tstops=0.0:5.0:80.0)
371+ plot(wp, title="NAND Gate DAE - Work-Precision (Low Tolerances)")
372+ ```
373+
374+ ```julia
375+ wp = WorkPrecisionSet(probs, abstols, reltols, setups; error_estimate=:l2,
376+ save_everystep=false, appxsol=refs,
377+ maxiters=Int(1e5), numruns=10,
323378 tstops=0.0:5.0:80.0)
324- plot(wp, title="NAND Gate DAE - Timeseries Error Analysis ")
379+ plot(wp, title="NAND Gate DAE - Timeseries Errors (Low Tolerances) ")
325380```
326381
327382## Analysis of Key Nodes
0 commit comments