Buck Converter: CCM Small-Signal Model

A buck converter is operated with the following parameters: $V_g = 20\,$V, $D = 0.5$, $L = 2\,$mH, $C = 100\mu$F, $R = 10\,\Omega$, and switching frequency of $10\,$kHz. The control ($D$) to output ($V_o$) transfer function of the buck converter is given by, $$ G_{vd}\,(s) = G_{d0}\,\displaystyle\frac{1} {1 + \displaystyle\frac{s}{Q\,\omega _0} + \left(\displaystyle\frac{s}{\omega _0}\right)^2} $$

Determine the following.

  1. $G_{d0}$
  2. $\omega _0$
  3. $Q$
Verify the small signal model by applying a step change in duty cycle (change duty cycle from 0.5 to 0.51) and plot the response from transfer function and from simulation on the same graph. Analytically determine the peak overshoot in the output voltage due to this step change.
In [1]:
from IPython.display import Image
Image(filename =r'buck_ccm_3_fig_1.png', width=320)
Out[1]:
No description has been provided for this image
In [2]:
# run this cell to view the circuit file.
%pycat buck_ccm_3_orig.in

We now replace the strings such as \$D1, \$D2, with the values of our choice by running the python script given below. It takes an existing circuit file buck_ccm_3_orig.in and produces a new circuit file buck_ccm_3.in, after replacing \$D1, \$D2, etc. with values of our choice. Note the use of the set_rparm statement in the solve block to equate the parameter D of the element named clock1 by the value of the variable y of the element named pwl. pwl generates a step change which is coupled to D of clock1 by the set_rparm statement.

In [3]:
import gseim_calc as calc
s_D1 = '0.5'
s_D2 = '0.51'
s_t1 = '25e-3'
s_t2 = '25.001e-3'

l = [
  ('$D1', s_D1),
  ('$D2', s_D2),
  ('$t1', s_t1),
  ('$t2', s_t2)
]
calc.replace_strings_1("buck_ccm_3_orig.in", "buck_ccm_3.in", l)
print('buck_ccm_3.in is ready for execution')
buck_ccm_3.in is ready for execution
Execute the following cell to run GSEIM on buck_ccm_3.in.
In [4]:
import os
import dos_unix
# uncomment for windows:
#dos_unix.d2u("buck_ccm_3.in")
os.system('run_gseim buck_ccm_3.in')
get_lib_elements: filename gseim_aux/xbe.aux
get_lib_elements: filename gseim_aux/ebe.aux
Circuit: filename = buck_ccm_3.in
Circuit: n_xbeu_vr = 2
Circuit: n_ebeu_nd = 4
main: i_solve = 0
main: calling solve_trns
Transient simulation starts...
i=0
i=10000
i=20000
i=30000
i=40000
solve_trns_exc completed.
GSEIM: Program completed.
Out[4]:
0

The circuit file (buck_ccm_3.in) is created in the same directory as that used for launching Jupyter notebook. The last step (i.e., running GSEIM on buck_ccm_3.in) creates a data file buck_ccm_3.dat in the same directory. We can now use the python code below to plot the output voltage versus time to ensure that the circuit is in a steady state before the step in $D$. If it is not, it would not make sense to compare the simulation results with the results (to be obtained in a later cell) of the buck converter small-signal model.

In [5]:
import numpy as np
import matplotlib.pyplot as plt 
import gseim_calc as calc
from setsize import set_size

slv = calc.slv("buck_ccm_3.in")

i_slv = 0
i_out = 0
filename = slv.l_filename_all[i_slv][i_out]
print('filename:', filename)
u = np.loadtxt(filename)
t1 = u[:, 0]
t = t1*1e3 # convert time to msec

col_IL = slv.get_index(i_slv,i_out,"IL")
col_IS = slv.get_index(i_slv,i_out,"IS")
col_ID = slv.get_index(i_slv,i_out,"ID")
col_IC = slv.get_index(i_slv,i_out,"IC")
col_v_in = slv.get_index(i_slv,i_out,"v_in")
col_v_out = slv.get_index(i_slv,i_out,"v_out")
col_clock = slv.get_index(i_slv,i_out,"clock")

color1='green'
color2='crimson'
color3='goldenrod'
color4='blue'

fig, ax = plt.subplots()
plt.subplots_adjust(wspace=0, hspace=0.0)

set_size(4, 2.5, ax) 

plt.grid(color='#CCCCCC', linestyle='solid', linewidth=0.5)

ax.plot(t, u[:,col_IL], color=color1, linewidth=1.0, label="$I_L$")

plt.xlabel('time (msec)', fontsize=11)

ax.legend(loc = 'lower right',frameon = True, fontsize = 10, title = None,
   markerfirst = True, markerscale = 1.0, labelspacing = 0.5, columnspacing = 2.0,
   prop = {'size' : 12},)

plt.tight_layout()
plt.show()
filename: buck_ccm_3.dat
No description has been provided for this image
In [6]:
import control as ct
import numpy as np
import matplotlib.pyplot as plt
import gseim_calc as calc
from setsize import set_size

# get values of Vin, D, etc from the circuit file:
fin = open("buck_ccm_3.in", "r")
for line in fin:
    if 'name=VS' in line:
        for s in line.split():
            if s.startswith('vdc='):
                Vin = float(s.split('=')[1])
                print('Vin:', Vin)
    if 'name=clock1' in line:
        for s in line.split():
            if s.startswith('f_hz='):
                f_hz = float(s.split('=')[1])
                print('f_hz:', f_hz)
    if 'name=pwl' in line:
        for s in line.split():
            if s.startswith('v1='):
                D1 = float(s.split('=')[1])
                print('D1:', D1)
            if s.startswith('v2='):
                D2 = float(s.split('=')[1])
                print('D2:', D2)
            if s.startswith('t1='):
                # t_step: time when D changes from D1 to D2
                t_step = float(s.split('=')[1])
                print('t_step:', t_step)
    if 'name=L' in line:
        for s in line.split():
            if s.startswith('l='):
                L = float(s.split('=')[1])
                print('L:', L)
    if 'name=C' in line:
        for s in line.split():
            if s.startswith('c='):
                C = float(s.split('=')[1])
                print('C:', C)
    if 'name=R' in line:
        for s in line.split():
            if s.startswith('r='):
                R = float(s.split('=')[1])
                print('R:', R)
    if 't_end=' in line:
        for s in line.split():
            if s.startswith('t_end='):
                t_end = float(s.split('=')[1])
                print('t_end:', t_end)

fin.close()

T = 1.0/f_hz

# Compute transfer function parameters:
Gd0 = Vin
w_0 = 1.0/np.sqrt(L*C)
Q = R*np.sqrt(C/L)

print('Gd0:', "%11.4E"%Gd0)
print('w_0:', "%11.4E"%w_0)
print('Q:', "%11.4E"%Q)

# construct transfer function of the buck converter small-signal model:
num = np.array([Gd0])
den = np.array([(1.0/(w_0*w_0)),(1.0/(Q*w_0)),1.0])
H1 = ct.tf(num, den)

# Compute step response, treating t_step as t=0.
t_end_1 = t_end - t_step
t_vec = np.linspace(0,t_end_1,200)

# xf stands for transfer function
t_xf, y_xf1 = ct.step_response(H1,t_vec)
# scale y_xf1, taking into account initial and final values of Vout
y_xf = D1*Vin + y_xf1*(D2-D1)
color1='green'
color2='crimson'
color3='cornflowerblue'
color4='blue'

fig, ax = plt.subplots(2, sharex=False)
plt.subplots_adjust(wspace=0, hspace=0.0)

set_size(6, 6, ax[0])

# plot Vout obtained by simulation:

slv = calc.slv("buck_ccm_3.in")

i_slv = 0
i_out = 0
filename = slv.l_filename_all[i_slv][i_out]
print('filename:', filename)
u = np.loadtxt(filename)

col_IL = slv.get_index(i_slv,i_out,"IL")
col_IS = slv.get_index(i_slv,i_out,"IS")
col_ID = slv.get_index(i_slv,i_out,"ID")
col_IC = slv.get_index(i_slv,i_out,"IC")
col_v_in = slv.get_index(i_slv,i_out,"v_in")
col_v_out = slv.get_index(i_slv,i_out,"v_out")
col_clock = slv.get_index(i_slv,i_out,"clock")

t_sim = u[:, 0] - t_step
l_t_sim = []
l_vout_sim = []

for i, t in enumerate(t_sim):
    if t >= 0.0:
        l_t_sim.append(t)
        l_vout_sim.append(u[:,col_v_out][i])

l1 = calc.avg_rms_1(np.array(l_t_sim), np.array(l_vout_sim), T)

# compute max value of v_out(avg) as obtained by simulation
vout_avg_max = -100.0
for i in range(len(l1[0])):
    if l1[1][i] > vout_avg_max:
        vout_avg_max = l1[1][i]
print('vout_avg_max (simulation):', "%11.4E"%vout_avg_max)
vout_avg_start = D1*Vin
print('vout_overshoot (simulation):', "%11.4E"%(vout_avg_max-vout_avg_start))

ax[0].grid(color='#CCCCCC', linestyle='solid', linewidth=0.5)
ax[0].set_xlim(left=0.0, right=t_end_1)
ax[0].set_xlabel('time (sec)', fontsize=11)
ax[0].plot(l_t_sim, l_vout_sim, color=color3, linewidth=1.0, label="$V_{out}^{sim}$")

# plot Vout obtained from transfer function:
ax[0].plot(t_xf, y_xf, color=color1, linewidth=1.0, label="$V_{out}^{XF}$")

# plot average value of Vout as obtained by simulation:
ax[0].plot(l1[0], l1[1], color=color2, linewidth=1.0, linestyle='--', dashes=(5,3), label="$V_{out}^{avg}$")

ax[0].legend(loc = 'lower right',frameon = True, fontsize = 10, title = None,
   markerfirst = True, markerscale = 1.0, labelspacing = 0.5, columnspacing = 2.0,
   prop = {'size' : 12},)

ax[1].grid(color='#CCCCCC', linestyle='solid', linewidth=0.5)
ax[1].set_xlim(left=0.0, right=0.005)
ax[1].set_xlabel('time (sec)', fontsize=11)
ax[1].plot(l_t_sim, l_vout_sim, color=color3, linewidth=1.0, label="$V_{out}^{sim}$")
ax[1].plot(t_xf, y_xf, color=color1, linewidth=1.0, label="$V_{out}^{XF}$")
ax[1].plot(l1[0], l1[1], color=color2, linewidth=1.0, linestyle='--', dashes=(5,3), label="$V_{out}^{avg}$")

ax[1].legend(loc = 'lower right',frameon = True, fontsize = 10, title = None,
   markerfirst = True, markerscale = 1.0, labelspacing = 0.5, columnspacing = 2.0,
   prop = {'size' : 12},)

plt.tight_layout()
plt.show()
Vin: 20.0
L: 0.002
C: 0.0001
R: 10.0
f_hz: 10000.0
t_step: 0.025
D1: 0.5
D2: 0.51
t_end: 0.04
Gd0:  2.0000E+01
w_0:  2.2361E+03
Q:  2.2361E+00
filename: buck_ccm_3.dat
vout_avg_max (simulation):  1.0297E+01
vout_overshoot (simulation):  2.9685E-01
No description has been provided for this image

This notebook was contributed by Prof. Nakul Narayanan K, Govt. Engineering College, Thrissur. He may be contacted at nakul@gectcr.ac.in.

In [ ]: