This section provides advanced examples from the examples folder in the root directory of SpiceGenTcl. Rather than focusing on basic operations, we highlight advanced use cases that showcase the full capabilities of both the package and the Tcl language. List of availible examples:
Monte-Carlo simulation - "examples/ngspice/advanced/monte_carlo.tcl" and "examples/xyce/advanced/monte_carlo.tcl" file
This example demonstrates multiple runs of a simple filter circuit and the collection of the resulting statistical distribution of frequency bandwidths. The original circuit source is from the ngspice source distribution. The target filter circuit is:
The filter is a 3rd-order Chebyshev bandpass. The first step is to build the circuit and obtain the magnitude of the transfer characteristic:
Here, we use a different method for creating a class instance: create instead of new. With create, we can directly set a custom object reference name, rather than relying on the automatically generated one by Tcl.
Keep in mind that c1 is an object reference, not the name of a variable storing the reference. Therefore, it can be used as an object command directly, without the need for a $.
To calculate the magnitude of the transfer function in dB scale from the output voltage phasor, we create a procedure:
calcDbMagVec
Procedure that apply calcDbMag to list of complex values.
Run and plot the result:
ticklEcharts !!!
We define pass bandwidth by edge values -10dB, to find them we use next procedure:
Find bandwidth:
The value is 1.086255 Mhz.
Our goal is to obtain a distribution of bandwidths by varying the filter parameters. To generate random values for these parameters, we use the built-in functions of the math::statistics package from Tcllib. The parameters can be distributed either normally or uniformly. For uniform distribution, we use ::math::statistics::random-uniform xmin xmax number; for normal distribution, we use ::math::statistics::random-normal mean stdev number. For uniform distribution, we define the following min and max limits for each C and L element:
We can specify different numbers of simulations; the more runs we perform, the more accurate the representation becomes. For example, we set the number of simulations to 1,000 runs with 15 intervals for constructing a boxplot:
Now we ready to run simulations 1000 times and collect results:
To obtain the distribution, we need to determine reasonable limits based on the minimum and maximum of the generated bandwidth values. Using the specified number of intervals (15), we apply the following procedure:
Here, we use the dedicated procedure from the statistics package: ::math::statistics::minmax-histogram-limits min max number. Additionally, we construct strings in form leftVal-rightVal that represent the intervals on the boxplot chart. We call this procedure and store the results:
Finally, to obtain the expected histogram for a uniform distribution, we use the following procedure with the built-in function: ::math::statistics::histogram-uniform xmin xmax limits number:
Call the createDist procedure to calculate the histogram y-axis values, which correspond to the number of bandwidths within each interval:
The same sequence of steps is applied for the normal distribution. We assume that std = (xmax - xmin) / 6, where xmax and xmin are the limits of the uniform distribution.
Finally, we plot resulted distributions:
ticklEcharts !!!
We can clearly see the difference between normal and uniform distributions; the intervals are close due to setting the standard deviation of the normal distribution as std = (xmax - xmin) / 6.
We can also take the uniform intervals and calculate the normal distribution values at these intervals:
ticklEcharts !!!
Parameters extraction of diode model parametersTop, Main, Index
In this advanced example we will do curve fitting procedure to extract parameters of diode model. We use measured (emulated with more advanced diode model generated data, with added small distorsion and make non-equal voltage steps) forward current characteristic of diode.
This example demands two additional Tcl packages to make fast interpolation and Levenberg-Marquardt optimization algorithm:
Also, the tclcsv package is used for data reading.
The main steps of this procedure are:
Create cost function that calculates difference between simulated and measured data, and which should be minimized
Select the region of optimization
Set list of parameters that will be extracted, define lower and upper limits for them.
Run optimization
Use resulted values as initial values for next optimization in different region
...
Repeat until the quality of fitting reaches required level.
First step is to define the cost function that will be used by the optimizer:
The input arguments are:
xall - list with fitting parameters, in our case, the parameters of the diode model.
pdata - the dictionary containing information that allows to calculate cost function, in our case it contains multiple fields that will be explained below.
args - any additional arguments, usually is used for passing information for calculating analytic derivatives.
Let me expand the values in pdata dictionary:
v - list of applied voltages values
i - list of measured current values
circuit - top circuit object simulating forward diode current
model - diode model object
vMin - start of the voltage region
vMax - end of the voltage region
vStep - size of voltage step
vSrc - voltage source object
First line creates variables with the same name as keys of pdata dictionary:
Then we run circuit with new parameters and read data back to calculate residuals:
Calculating residuals and save simulated data in the loop:
The residuals is calculated with the next formula:
We use logarithm of values because the current values span across multiple orders, from 1e-8 to 1e-2, that creates issue for fitting algorithm.
Procedure returns dictionary that contains lists with residuals and diode current under the keys fvec and fval, and fvec key is mandatory because list under this key is used by optimization procedure.
To run simulation we need to build circuit and define necessary objects:
We save object references for diode model and voltage source into dedicated variable to use it in cost function - it will be saved into pdata dictionary.
Next, we read measured data from .csv file with tclcsv package:
The loaded data is (with selected regions of fitting):
First region we want to fit is the region of ideal diode, it spans from minimum voltage to approximately 0.85V. We set these limits in variables vMin, vMax and define the equal step of voltage vStep.
We use fixed voltage step instead of data points defined in measured data with non-equal steps to ease the defining the sweep of voltage in simulator - we can use DC sweep of voltage source with fixed step. So, we use linear interpolation to get values of current at even grid with defined minimum and maximum values. Then, we create pdata dictionary with corresponding values:
Command lin1d is imported from tclinterp package.
Parameters that we need to find (extract) are:
is
Saturation current.
n
Ideality factor.
rs
Series resistance.
ikf
Forward knee current.
In first region the parameters of interest are n and is0 - it affects current curve the most in this region.
Parameter are represented as special objects ::tclopt::ParameterMpfit from tclopt package, where we can define initial values and lower/upper limits:
For rs and ikf parameters we specify option -fixed to make them constant during the first region fitting.
Optimization is done with Levenberg-Marquardt algorithm based optimizator from tclopt package. First step is to create ::tclopt::Mpfit object that defines the optimization parameters:
The arguments we pass here are:
-funct
Name of the procedure calculating the residuals.
-m
Number of fitting points.
-pdata
Dictionary that is passed to cost function procedure.
Then we add parameters to $optimizer object, and the order of adding them should match the order of the elements list xall we pass values to cost function:
Then we can run the simulation, read the resulted list of parameters x and print formatted values:
The result is:
You can notice that is and n now have different values, and rs and ikf remains the same because of -fixed option.
Now we are ready to fit curve in the second ohmic/high-injection region. It started at the upper end of first region and continue until the end of the data. We interpolate this range with the same voltage step:
We will use the same pdata variable containing dictionary, but change the current and voltage data and limits:
We make is and n parameters fixed this time, and free the rs and ikf:
You can notice that we supply the resulted values of is and n as the initial values to the second fitting, so we have two parameters already fitted before start the next optimization.
Replace $optimizer object fields with the new values:
Run, read result and print new parameters values:
The result is:
Now we can see that rs and ikf were changed during fitting, just as planned.