Tcl SpiceGenTcl package (v0.61)

AdvancedTop, Main, Index

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 simulationTop, Main, Index

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:

drawing

The filter is a 3rd-order Chebyshev bandpass. The first step is to build the circuit and obtain the magnitude of the transfer characteristic:

# create top-level circuit
set circuit [Circuit new {Monte-Carlo}]
# add elements to circuit
$circuit add [Vac new 1 n001 0 -ac 1]
$circuit add [R new 1 n002 n001 -r 141]
$circuit add [R new 2 0 out -r 141]
C create c1 1 out 0 -c 1e-9
L create l1 1 out 0 -l 10e-6
C create c2 2 n002 0 -c 1e-9
L create l2 2 n002 0 -l 10e-6
C create c3 3 out n003 -c 250e-12
L create l3 3 n003 n002 -l 40e-6
foreach elem [list c1 l1 c2 l2 c3 l3] {
    $circuit add $elem
}
$circuit add [Ac new -variation oct -n 100 -fstart 250e3 -fstop 10e6]
#set simulator with default 
set simulator [Batch new {batch1}]
# attach simulator object to circuit
$circuit configure -simulator $simulator

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.

C create c1 1 out 0 -c 1e-9

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:

proc calcDbMag {re im} {
    set mag [= {sqrt($re*$re+$im*$im)}]
    set db [= {10*log($mag)}]
    return $db
}

proc calcDbMagVec {vector} {
    foreach value $vector {
        lappend db [calcDbMag [@ $value 0] [@ $value 1]]
    }
    return $db
}
calcDbMagVecProcedure that apply calcDbMag to list of complex values.

Run and plot the result:

# run simulation
$circuit runAndRead
# get data dictionary
set data [$circuit getDataDict]
set trace [calcDbMagVec [dget $data v(out)]]
set freqs [dget $data frequency]
foreach x $freqs y $trace {
    lappend xydata [list [@ $x 0] $y]
}
puts [findBW [lmap freq $freqs {@ $freq 0}] $trace -10]
set chartTransMag [ticklecharts::chart new]
$chartTransMag Xaxis -name "Frequency, Hz" -minorTick {show "True"} -type "log" -splitLine {show "True"}
$chartTransMag Yaxis -name "Magnitude, dB" -minorTick {show "True"} -type "value" -splitLine {show "True"}
$chartTransMag SetOptions -title {} -tooltip {trigger "axis"} -animation "False" -toolbox {feature {dataZoom {yAxisIndex "none"}}} -grid {left "10%" right "15%"} -backgroundColor "#212121"
$chartTransMag Add "lineSeries" -data $xydata -showAllSymbol "nothing" -symbolSize "1"
set fbasename [file rootname [file tail [info script]]]
$chartTransMag Render -outfile [file normalize [file join .. html_charts ${fbasename}_typ.html]] -width 1000px

ticklEcharts !!!

We define pass bandwidth by edge values -10dB, to find them we use next procedure:

proc findBW {freqs vals trigVal} {
    # calculate bandwidth of results
    set freqsLen [llength $freqs]
    for {set i 0} {$i<$freqsLen} {incr i} {
        set iVal [@ $vals $i]
        set ip1Val [@ $vals [+ $i 1]]
        if {($iVal<=$trigVal) && ($ip1Val>=$trigVal)} {
            set freqStart [@ $freqs $i]
        } elseif {($iVal>=$trigVal) && ($ip1Val<=$trigVal)} {
            set freqEnd [@ $freqs $i]
        }
    }
    set bw [= {$freqEnd-$freqStart}]
    return $bw
}

Find bandwidth:

puts [findBW [lmap freq $freqs {@ $freq 0}] $trace -10]

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:

set distLimits [dcreate c1 [dcreate min 0.9e-9 max 1.1e-9] l1 [dcreate min 9e-6 max 11e-6] c2 [dcreate min 0.9e-9 max 1.1e-9] l2 [dcreate min 9e-6 max 11e-6] c3 [dcreate min 225e-12 max 275e-12] l3 [dcreate min 36e-6 max 44e-6]]

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:

# set number of simulations
set mcRuns 100
set numOfIntervals 15

Now we ready to run simulations 1000 times and collect results:

# loop in which we run simulation with uniform distribution
for {set i 0} {$i<$mcRuns} {incr i} {
    #set elements values according to uniform distribution
    foreach elem [list c1 l1 c2 l2 c3 l3] {
        $elem setParamValue [string index $elem 0] [random-uniform {*}[dict values [dget $distLimits $elem]] 1]
    }
    # run simulation
    $circuit runAndRead
    # get data dictionary
    set data [$circuit getDataDict]
    # get results
    if {$i==0} {
        set freqs [dget $data frequency]
        foreach freq $freqs {
            lappend freqRes [@ $freq 0]
        }
    }
    # get vout frequency curve
    lappend traceListUni [calcDbMagVec [dget $data v(out)]]
    # calculate bandwidths values
    lappend bwsUni [findBW $freqRes [@ $traceListUni end] -10]
}

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:

proc createIntervals {data numOfIntervals} {
    set intervals [::math::statistics::minmax-histogram-limits [tcl::mathfunc::min {*}$data]  [tcl::mathfunc::max {*}$data] $numOfIntervals]
    lappend intervalsStrings [format "<=%.2e" [@ $intervals 0]]
    for {set i 0} {$i<[- [llength $intervals] 1]} {incr i} {
        lappend intervalsStrings [format "%.2e-%.2e" [@ $intervals $i] [@ $intervals [+ $i 1]]]
    }
    return [dcreate intervals $intervals intervalsStr $intervalsStrings]
}

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:

set uniIntervals [createIntervals $bwsUni $numOfIntervals]

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:

proc createDist {data intervals} {
    set dist [::math::statistics::histogram $intervals $data]
    return [lrange $dist 0 end-1]
}

Call the createDist procedure to calculate the histogram y-axis values, which correspond to the number of bandwidths within each interval:

set normDist [createDist $bwsNorm [dget $normIntervals intervals]]

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.

# set parameter's normal distributions limits
set normalLimits [dcreate c1 [dcreate mean 1e-9 std [/ 0.1e-9 3]] l1 [dcreate mean 10e-6 std [/ 1e-6 3]] c2 [dcreate mean 1e-9 std [/ 0.1e-9 3]] l2 [dcreate mean 10e-6 std [/ 1e-6 3]] c3 [dcreate mean 250e-12 std [/ 25e-12 3]] l3 [dcreate mean 40e-6 std [/ 4e-6 3]]]

## loop in which we run simulation with normal distribution
for {set i 0} {$i<$mcRuns} {incr i} {
    #set elements values according to normal distribution
    foreach elem [list c1 l1 c2 l2 c3 l3] {
        $elem setParamValue [string index $elem 0] [random-normal {*}[dict values [dget $normalLimits $elem]] 1]
    }
    # run simulation
    $circuit runAndRead
    # get data dictionary
    set data [$circuit getDataDict]
    # get results
    if {$i==0} {
        set freqs [dget $data frequency]
        foreach freq $freqs {
            lappend freqRes [@ $freq 0]
        }
    }
    # get vout frequency curve
    lappend traceListNorm [calcDbMagVec [dget $data v(out)]]
    # calculate bandwidths values
    lappend bwsNorm [findBW $freqRes [@ $traceListNorm end] -10]
}
# get distribution of bandwidths with normal parameters distribution
set normIntervals [createIntervals $bwsNorm $numOfIntervals]
set normDist [createDist $bwsNorm [dget $normIntervals intervals]]

Finally, we plot resulted distributions:

# plot results with ticklecharts
# chart for uniformly distributed parameters
set chartUni [ticklecharts::chart new]
$chartUni Xaxis -name "Frequency intervals, Hz" -data [list [dget $uniIntervals intervalsStr]] -axisTick {show "True" alignWithLabel "True"} -axisLabel {interval "0" rotate "45" fontSize "8"}
$chartUni Yaxis -name "Bandwidths per interval" -minorTick {show "True"} -type "value"
$chartUni SetOptions -title {} -tooltip {trigger "axis"} -animation "False" -toolbox {feature {dataZoom {yAxisIndex "none"}}} -backgroundColor "#212121"
$chartUni Add "barSeries" -data [list $uniDist]
# chart for normally distributed parameters
set chartNorm [ticklecharts::chart new]
$chartNorm Xaxis -name "Frequency intervals, Hz" -data [list [dget $normIntervals intervalsStr]] -axisTick {show "True" alignWithLabel "True"} -axisLabel {interval "0" rotate "45" fontSize "8"}
$chartNorm Yaxis -name "Bandwidths per interval" -minorTick {show "True"} -type "value"
$chartNorm SetOptions -title {} -tooltip {trigger "axis"} -animation "False" -toolbox {feature {dataZoom {yAxisIndex "none"}}} -backgroundColor "#212121"
$chartNorm Add "barSeries" -data [list $normDist]
# create multiplot
set layout [ticklecharts::Gridlayout new]
$layout Add $chartNorm -bottom "10%" -height "35%" -width "75%"
$layout Add $chartUni -bottom "60%" -height "35%" -width "75%"

set fbasename [file rootname [file tail [info script]]]
$layout Render -outfile [file normalize [file join .. html_charts $fbasename.html]] -height 800px -width 1200px

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:

# find distribution of normal distributed values in uniform intervals       
set normDistWithUniIntervals [createDist $bwsNorm [dget $uniIntervals intervals]]
set chartCombined [ticklecharts::chart new]
$chartCombined Xaxis -name "Frequency intervals, Hz" -data [list [dget $uniIntervals intervalsStr]] -axisTick {show "True" alignWithLabel "True"} -axisLabel {interval "0" rotate "45" fontSize "8"}
$chartCombined Yaxis -name "Bandwidths per interval" -minorTick {show "True"} -type "value"
$chartCombined SetOptions -title {} -legend {} -tooltip {trigger "axis"} -animation "False" -toolbox {feature {dataZoom {yAxisIndex "none"}}} -grid {left "10%" right "15%"} -backgroundColor "#212121"
$chartCombined Add "barSeries" -data [list $uniDist] -name "Uniform"
$chartCombined Add "barSeries" -data [list $normDistWithUniIntervals] -name "Normal"
$chartCombined Render -outfile [file normalize [file join .. html_charts ${fbasename}_combined.html]] -height 800px -width 1200px

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:

First step is to define the cost function that will be used by the optimizer:

proc diodeIVcalc {xall pdata args} {
    dict with pdata {}
    $model setParamValue is [@ $xall 0] n [@ $xall 1] rs [@ $xall 2] ikf [@ $xall 3]
    $vSrc setParamValue start $vMin stop $vMax incr $vStep
    $circuit runAndRead
    set data [$circuit getDataDict]
    foreach iVal $i iSim [lmap i [dget $data i(va)] {= {-$i}}] {
        lappend fvec [= {log(abs($iVal))-log(abs($iSim))}]
        lappend fval $iSim
    }
    return [dcreate fvec $fvec fval $fval]
}

The input arguments are:

Let me expand the values in pdata dictionary:

First line creates variables with the same name as keys of pdata dictionary:

dict with pdata {}

Then we set parameters values of the diode model object using ::SpiceGenTcl::Model::setParamValue method:

$model setParamValue is [@ $xall 0] n [@ $xall 1] rs [@ $xall 2] ikf [@ $xall 3]

The same we do with voltage source element:

$vSrc setParamValue start $vMin stop $vMax incr $vStep

Then we run circuit with new parameters and read data back to calculate residuals:

$circuit runAndRead
set data [$circuit getDataDict]

Calculating residuals and save simulated data in the loop:

foreach iVal $i iSim [lmap i [dget $data i(va)] {= {-$i}}] {
    lappend fvec [= {log(abs($iVal))-log(abs($iSim))}]
    lappend fval $iSim
}

The residuals is calculated with the next formula:

f = log ⎛|i    |⎞ - log ⎛|i   |⎞
        ⎝| meas|⎠       ⎝| sim|⎠

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:

set diodeModel [DiodeModel new diomod -is 1e-12 -n 1.0 -rs 30 -cj0 1e-9 -trs1 0.001 -xti 5 -ikf 1e-4]
set vSrc [Dc new -src va -start 0 -stop 2 -incr 0.02]
set circuit [Circuit new {diode IV}]
$circuit add [D new 1 anode 0 -model diomod -area 1]
$circuit add [Vdc new a anode 0 -dc 0]
$circuit add $diodeModel
$circuit add $vSrc
set tempSt [Temp new 25]
$circuit add $tempSt
$circuit configure -simulator [Batch new {batch1}]

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:

set file [open [file join $fileDataPath iv25.csv]]
set ivTemp25 [csv_read -startline 1 $file]
close $file
set vRaw [lmap elem $ivTemp25 {@ $elem 0}]
set iRaw [lmap elem $ivTemp25 {@ $elem 1}]

The loaded data is (with selected regions of fitting):

drawing

Fitting in first regionTop, Main, Index

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.

set vMin [min {*}$vRaw]
set vMax 0.85
set vStep 0.02

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:

set vInterp [lseq $vMin to $vMax by $vStep]
set iInterp [lin1d -x $vRaw -y $iRaw -xi $vInterp]
set pdata [dcreate v $vInterp i $iInterp circuit $circuit model $diodeModel vMin $vMin vMax $vMax vStep $vStep vSrc $vSrc]

Command lin1d is imported from tclinterp package.

Parameters that we need to find (extract) are:

isSaturation current.
nIdeality factor.
rsSeries resistance.
ikfForward 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:

set iniPars [list 1e-14 1.0 30 1e-4]
set par0 [::tclopt::ParameterMpfit new is [@ $iniPars 0] -lowlim 1e-17 -uplim 1e-12]
set par1 [::tclopt::ParameterMpfit new n [@ $iniPars 1] -lowlim 0.5 -uplim 2]
set par2 [::tclopt::ParameterMpfit new rs [@ $iniPars 2] -fixed -lowlim 1e-10 -uplim 100]
set par3 [::tclopt::ParameterMpfit new ikf [@ $iniPars 3] -fixed -lowlim 1e-12 -uplim 0.1]

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:

set optimizer [::tclopt::Mpfit new -funct diodeIVcalc -m [llength $vInterp] -pdata $pdata]

The arguments we pass here are:

-functName of the procedure calculating the residuals.
-mNumber of fitting points.
-pdataDictionary 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:

$optimizer addPars $par0 $par1 $par2 $par3

Then we can run the simulation, read the resulted list of parameters x and print formatted values:

set result [$optimizer run]
set resPars [dget $result x]
puts [format "is=%.3e, n=%.3e, rs=%.3e, ikf=%.3e" {*}[dget $result x]]

The result is:

is=5.771e-17, n=1.050e+00, rs=3.000e+01, ikf=1.000e-04

You can notice that is and n now have different values, and rs and ikf remains the same because of -fixed option.

Fitting in second regionTop, Main, Index

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:

set vMin 0.85
set vMax [max {*}$vRaw]
set vInterp [lseq $vMin to $vMax by $vStep]
set iInterp [lin1d -x $vRaw -y $iRaw -xi $vInterp]

We will use the same pdata variable containing dictionary, but change the current and voltage data and limits:

dset pdata v $vInterp
dset pdata i $iInterp
dset pdata vMin $vMin
dset pdata vMax $vMax

We make is and n parameters fixed this time, and free the rs and ikf:

$par0 configure -fixed 1 -initval [@ $resPars 0]
$par1 configure -fixed 1 -initval [@ $resPars 1]
$par2 configure -fixed 0
$par3 configure -fixed 0

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:

$optimizer configure -m [llength $vInterp] -pdata $pdata

Run, read result and print new parameters values:

set result [$optimizer run]
set fittedIdiode [dget [diodeIVcalc [dget $result x] $pdata] fval]
set resPars [dget $result x]
puts [format "is=%.3e, n=%.3e, rs=%.3e, ikf=%.3e" {*}[dget $result x]]

The result is:

is=5.771e-17, n=1.050e+00, rs=5.430e+00, ikf=3.069e-04

Now we can see that rs and ikf were changed during fitting, just as planned.

Fitting the whole curveTop, Main, Index

The last step is the fitting to the whole curve, with initial values of parameters replaced with resulted values.

Again, set voltage limits to minimum and maximum of the data, interpolate it and replace data in $pdata dictionary:

set vMin [min {*}$vRaw]
set vMax [max {*}$vRaw]
set vInterp [lseq $vMin to $vMax by $vStep]
set iInterp [lin1d -x $vRaw -y $iRaw -xi $vInterp]
dset pdata v $vInterp
dset pdata i $iInterp
dset pdata vMin $vMin
dset pdata vMax $vMax

We unfix rs and ikf parameters, and set new limits within ±10% of previous values to forbid parameters becoming too different:

$par0 configure -fixed 0 -initval [@ $resPars 0] -lowlim [= {[@ $resPars 0]*0.9}] -uplim [= {[@ $resPars 0]*1.1}]
$par1 configure -fixed 0 -initval [@ $resPars 1] -lowlim [= {[@ $resPars 1]*0.9}] -uplim [= {[@ $resPars 1]*1.1}]
$par2 configure -initval [@ $resPars 2] -lowlim [= {[@ $resPars 2]*0.9}] -uplim [= {[@ $resPars 2]*1.1}]
$par3 configure -initval [@ $resPars 3] -lowlim [= {[@ $resPars 3]*0.9}] -uplim [= {[@ $resPars 3]*1.1}]

The set new data in optimizer object, run and print results:

$optimizer configure -m [llength $vInterp] -pdata $pdata
set result [$optimizer run]
puts [format "is=%.3e, n=%.3e, rs=%.3e, ikf=%.3e" {*}[dget $result x]]

The result is:

is=6.348e-17, n=1.061e+00, rs=5.232e+00, ikf=3.376e-04

All parameters were changed to produce best fit for the whole curve.

Plotting resulted dataTop, Main, Index

Now we are ready to see the result of the fitting, we need to calculate curves with initial values and final values of parameters:

set initIdiode [dget [diodeIVcalc $iniPars $pdata] fval]
set fittedVIdiode [lmap vVal $vInterp iVal $fittedIdiode {list $vVal $iVal}]
set initVIdiode [lmap vVal $vInterp iVal $initIdiode {list $vVal $iVal}]
set viRaw [lmap vVal $vRaw iVal $iRaw {list $vVal $iVal}]

Plot it with ticklecharts package in linear and logarithmic scales:

set chart [ticklecharts::chart new]
$chart Xaxis -name "v(anode), V" -minorTick {show "True"}  -type "value" -splitLine {show "True"} -min "0.4" -max "1.6"
$chart Yaxis -name "Idiode, A" -minorTick {show "True"}  -type "value" -splitLine {show "True"} -min "0.0" -max "dataMax"
$chart SetOptions -title {} -tooltip {trigger "axis"} -animation "False" -legend {} -toolbox {feature {dataZoom {yAxisIndex "none"}}} -grid {left "10%" right "15%"} -backgroundColor "#212121" 
$chart Add "lineSeries" -data $fittedVIdiode -showAllSymbol "nothing" -name "fitted" -symbolSize "4" 
$chart Add "lineSeries" -data $initVIdiode -showAllSymbol "nothing" -name "unfitted" -symbolSize "4"
$chart Add "lineSeries" -data $viRaw -showAllSymbol "nothing" -name "measured" -symbolSize "4"
set chartLog [ticklecharts::chart new]
$chartLog Xaxis -name "v(anode), V" -minorTick {show "True"}  -type "value" -splitLine {show "True"} -min "0.4" -max "1.6"
$chartLog Yaxis -name "Idiode, A" -minorTick {show "True"}  -type "log" -splitLine {show "True"} -min "dataMin" -max "0.1"
$chartLog SetOptions -title {} -tooltip {trigger "axis"} -animation "False" -legend {} -toolbox {feature {dataZoom {yAxisIndex "none"}}} -grid {left "10%" right "15%"} -backgroundColor "#212121"
$chartLog Add "lineSeries" -data $fittedVIdiode -showAllSymbol "nothing" -name "fitted" -symbolSize "4"
$chartLog Add "lineSeries" -data $initVIdiode -showAllSymbol "nothing" -name "unfitted" -symbolSize "4"
$chartLog Add "lineSeries" -data $viRaw -showAllSymbol "nothing" -name "measured" -symbolSize "4"

set layout [ticklecharts::Gridlayout new]
$layout Add $chartLog -bottom "5%" -height "40%" -width "80%"
$layout Add $chart -bottom "55%" -height "40%" -width "80%"

set fbasename [file rootname [file tail [info script]]]
$layout Render -outfile [file normalize [file join .. html_charts $fbasename.html]] -height 900px -width 700px

ticklEcharts !!!

On the plot we can see the initial simulated curve, the measured data and the final fitting result.