While reading Dmitry Nizh’s great site, I found an article on parameter optimization using SPICE. In Dmitry’s case, he used an AWK script to pipe the output of the distortion analysis from SPICE to a plotting program, so the harmonics vs. power curves could be displayed and further analysed. To see what else can be done with SPICE, I googled “SPICE circuit optimization” and saw a bunch of papers published by Burmen and Tuma of University of Slovenia, which let me to discover their version of the Berkeley/XSPICE port for Windows – SpiceOpus, that contains a built-in optimize function. After going over some of the examples shown on the SpiceOpus website, I managed to try out the optimize function on a simple SPUD amp, the same one that Dmitry used in his SPICE optimization example. Here are few lines of code that I used for the optimization for both the preamp plate load and power amp load:
SpiceOpus (c) 45 -> define error(f) db(v(3)/230) - db(v(8)) SpiceOpus (c) 46 -> optimize parameter 0 @r6[resistance] low 10k high 50k initial 40k SpiceOpus (c) 47 -> optimize parameter 1 @r7[resistance] low 2k high 10k initial 5k SpiceOpus (c) 48 -> optimize analysis 0 ac dec 10 100 10k SpiceOpus (c) 49 -> optimize implicit 0 ac1.v(3) gt 230 SpiceOpus (c) 50 -> optimize cost mean(mag(error(frequency))^2) SpiceOpus (c) 51 -> optimize method complex SpiceOpus (c) 52 -> optimize Complex: stopped, simplex small enough. Time needed for optimisation: 0.062 seconds Number of iterations: 42 Lowest cost function value: 4.926709e-002 Optimal values: @r6[resistance] = 4.006276e+004 @r7[resistance] = 4.519619e+003
The results were very close to what Dmitry came up with, so I guess it worked. Since I am just using the ac sweep response as a proxy for distortion, I think perhaps the result could be further improved if I can figure out how to provide a more precise cost function to the optimization routine.
Since SpiceOpus has a discrete Fourier transform function, it is possible to calculate the total harmonic distortion without having to export the data to another program for processing. The THD can be calculated as follows:
Then the optimization codes can be written as follows:
SpiceOpus (c) 1 -> source 6f5p_opt.cir SpiceOpus (c) 2 -> save v(10) SpiceOpus (c) 3 -> optimize parameter 0 @r6[resistance] low 10k high 50k initial 40k SpiceOpus (c) 4 -> optimize parameter 1 @r7[resistance] low 4 high 16 initial 8 SpiceOpus (c) 5 -> optimize analysis 0 tran 1u 1m SpiceOpus (c) 6 -> optimize analysis 1 linearize 1u SpiceOpus (c) 7 -> optimize analysis 2 spec 0 5k 1k v(10) SpiceOpus (c) 8 -> optimize cost ((abs(v(10))^2+abs(v(10))^2+abs(v(10))^2+abs(v(10))^2)/abs(v(10))^2)^0.5 SpiceOpus (c) 9 -> optimize method complex SpiceOpus (c) 10 -> optimize Warning: Convergence problems at node (1). Complex: stopped, simplex small enough. Time needed for optimisation: 16.765 seconds Number of iterations: 48 Lowest cost function value: 3.290727e-001 Optimal values: @r6[resistance] = 3.998431e+004 @r7[resistance] = 6.879497e+000
R7 is the speaker load, so the equivalent plate load is 4.3k. Are the load resistances really “optimal”? Can’t tell until I build the circuit and test it on the bench. But it does look promising & fast too!