8.2.2. Diode Recovery Time

diode-recovery-time.pydiode-recovery-time.py
#!# This example illustrates the diode recovery time and the capacitive behaviour of a PN junction.

# Fixme: Split the plots ? Add some explanations at the end

####################################################################################################

import os

import numpy as np
import matplotlib.pyplot as plt

####################################################################################################

import PySpice.Logging.Logging as Logging
logger = Logging.setup_logging()

####################################################################################################

from PySpice.Probe.Plot import plot
from PySpice.Spice.Library import SpiceLibrary
from PySpice.Spice.Netlist import Circuit
from PySpice.Unit import *

####################################################################################################

libraries_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'libraries')
spice_library = SpiceLibrary(libraries_path)

####################################################################################################

#!# Let define some parameters

dc_offset = 1@u_V
ac_amplitude = 100@u_mV

####################################################################################################

#!# We will first compute some quiescent points and the corresponding dynamic resistance.

#cm# diode-characteristic-curve-circuit.m4

#!# Since this circuit is equivalent to a voltage divider, we can write the following relation :
#!#
#!# .. math::
#!#
#!#     V_{out} = \frac{Z_d}{R_1 + Z_d} V_{in}
#!#
#!# where :math:`Z_d` is the diode impedance.

circuit = Circuit('Diode')
circuit.include(spice_library['BAV21'])
# Fixme: Xyce: Device model BAV21: Illegal parameter(s) given for level 1 diode: IKF
source = circuit.V('input', 'in', circuit.gnd, dc_offset)
circuit.R(1, 'in', 'out', 1@u_kΩ)
circuit.D('1', 'out', circuit.gnd, model='BAV21')

quiescent_points = []
for voltage in (dc_offset - ac_amplitude, dc_offset, dc_offset + ac_amplitude):
    source.dc_value = voltage
    simulator = circuit.simulator(temperature=25, nominal_temperature=25)
    analysis = simulator.operating_point()
    # Fixme: handle unit
    quiescent_voltage = float(analysis.out)
    quiescent_current = - float(analysis.Vinput)
    quiescent_points.append(dict(voltage=voltage,
                                 quiescent_voltage=quiescent_voltage,
                                 quiescent_current=quiescent_current))
    print("Quiescent Point {:.1f} mV {:.1f} mA".format(quiescent_voltage*1e3, quiescent_current*1e3))
#o#

dynamic_resistance = ((quiescent_points[ 0]['quiescent_voltage'] -
                       quiescent_points[-1]['quiescent_voltage'])
                      /
                      (quiescent_points[ 0]['quiescent_current'] -
                       quiescent_points[-1]['quiescent_current']))

#?# print("Dynamic Resistance = {:.1f} Ω".format(dynamic_resistance))
#?# #o#

#!# We found a dynamic resistance of @<@dynamic_resistance:.1f@>@ Ω.

####################################################################################################

#!#
#!# We will now drive the diode with a sinusoidal source and perform an AC analysis.

#cm# diode-characteristic-curve-circuit-ac.m4

circuit = Circuit('Diode')
circuit.include(spice_library['BAV21'])
circuit.Sinusoidal('input', 'in', circuit.gnd,
                   dc_offset=dc_offset, offset=dc_offset,
                   amplitude=ac_amplitude)
R = circuit.R(1, 'in', 'out', 1@u_kΩ)
circuit.D('1', 'out', circuit.gnd, model='BAV21')

simulator = circuit.simulator(temperature=25, nominal_temperature=25)
analysis = simulator.ac(start_frequency=10@u_kHz, stop_frequency=1@u_GHz, number_of_points=10,  variation='dec')

#!# Let plot the voltage across the diode and the dynamic resistance as a function of the frequency.

figure = plt.figure(1, (20, 10))

axe = plt.subplot(311)
# Fixme: handle unit in plot (scale and legend)
axe.semilogx(analysis.frequency, np.absolute(analysis.out)*1e3)
axe.grid(True)
axe.grid(True, which='minor')
axe.set_xlabel("Frequency [Hz]")
axe.set_ylabel("Vd [mV]")

axe = plt.subplot(312)
current = (analysis['in'] - analysis.out) / float(R.resistance)
axe.semilogx(analysis.frequency, np.absolute(analysis.out/current))
axe.grid(True)
axe.grid(True, which='minor')
axe.set_xlabel("Frequency [Hz]")
axe.set_ylabel('Rd [Ω]')

####################################################################################################

#!# We will now drive the diode with a pulse generator and perform a transient analysis.

#cm# diode-characteristic-curve-circuit-pulse.m4

frequency = 1@u_MHz

circuit = Circuit('Diode')
circuit.include(spice_library['BAV21'])
# source = circuit.Sinusoidal('input', 'in', circuit.gnd,
#                             dc_offset=dc_offset, offset=dc_offset,
#                             amplitude=ac_amplitude,
#                             frequency=frequency)
source = circuit.Pulse('input', 'in', circuit.gnd,
                       initial_value=dc_offset-ac_amplitude, pulsed_value=dc_offset+ac_amplitude,
                       pulse_width=frequency.period/2, period=frequency.period)
circuit.R(1, 'in', 'out', 1@u_kΩ)
circuit.D('1', 'out', circuit.gnd, model='BAV21')

simulator = circuit.simulator(temperature=25, nominal_temperature=25)
analysis = simulator.transient(step_time=source.period/1e3, end_time=source.period*4)

axe = plt.subplot(313)
# Fixme: axis, x scale
# plot(analysis['in'] - dc_offset + quiescent_points[0]['quiescent_voltage'], axis=axe)
# plot(analysis.out, axis=axe)
axe.plot(analysis.out.abscissa*1e6, analysis.out)
axe.legend(('Vin [V]', 'Vout [V]'), loc=(.8,.8))
axe.grid()
axe.set_xlabel('t [μs]')
axe.set_ylabel('[V]')
# axe.set_ylim(.5, 1 + ac_amplitude + .1)

plt.tight_layout()
plt.show()

#fig# save_figure(figure, 'diode-recovery-time.png')

#!# We notice the output of the circuit cannot follow the pulse generator.  It is due to the
#!# capacitive behaviour of a PN junction that cut off the highest frequencies of the pulse.  The
#!# plot of the dynamic resistance as a function of the frequency show a typical low pass filter
#!# behaviour where the impedance drop at high frequencies.

This example illustrates the diode recovery time and the capacitive behaviour of a PN junction.

# Fixme: Split the plots ? Add some explanations at the end

import os

import numpy as np
import matplotlib.pyplot as plt

import PySpice.Logging.Logging as Logging
logger = Logging.setup_logging()

from PySpice.Probe.Plot import plot
from PySpice.Spice.Library import SpiceLibrary
from PySpice.Spice.Netlist import Circuit
from PySpice.Unit import *

libraries_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'libraries')
spice_library = SpiceLibrary(libraries_path)

Let define some parameters

dc_offset = 1@u_V
ac_amplitude = 100@u_mV

We will first compute some quiescent points and the corresponding dynamic resistance.

../../_images/diode-characteristic-curve-circuit.png

Since this circuit is equivalent to a voltage divider, we can write the following relation :

\[V_{out} = \frac{Z_d}{R_1 + Z_d} V_{in}\]

where \(Z_d\) is the diode impedance.

circuit = Circuit('Diode')
circuit.include(spice_library['BAV21'])
# Fixme: Xyce: Device model BAV21: Illegal parameter(s) given for level 1 diode: IKF
source = circuit.V('input', 'in', circuit.gnd, dc_offset)
circuit.R(1, 'in', 'out', 1@u_kΩ)
circuit.D('1', 'out', circuit.gnd, model='BAV21')

quiescent_points = []
for voltage in (dc_offset - ac_amplitude, dc_offset, dc_offset + ac_amplitude):
    source.dc_value = voltage
    simulator = circuit.simulator(temperature=25, nominal_temperature=25)
    analysis = simulator.operating_point()
    # Fixme: handle unit
    quiescent_voltage = float(analysis.out)
    quiescent_current = - float(analysis.Vinput)
    quiescent_points.append(dict(voltage=voltage,
                                 quiescent_voltage=quiescent_voltage,
                                 quiescent_current=quiescent_current))
    print("Quiescent Point {:.1f} mV {:.1f} mA".format(quiescent_voltage*1e3, quiescent_current*1e3))
2017-09-17 17:32:09,622 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP947 1.000
2017-09-17 17:32:09,623 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP953 1.000
2017-09-17 17:32:09,623 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP949 1.000
2017-09-17 17:32:09,623 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP955 1.000
2017-09-17 17:32:09,623 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP951 1.000
2017-09-17 17:32:09,624 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP952 1.000
2017-09-17 17:32:09,624 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP948 1.000
2017-09-17 17:32:09,624 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP954 1.000
2017-09-17 17:32:09,624 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP950 1.000
2017-09-17 17:32:09,624 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BDP956 1.000
2017-09-17 17:32:09,625 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BCP70 1.000
2017-09-17 17:32:09,625 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    4 5 6 BCP72 1.000
2017-09-17 17:32:09,625 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC327 1.000
2017-09-17 17:32:09,625 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC328 1.000
2017-09-17 17:32:09,626 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC807 1.000
2017-09-17 17:32:09,626 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC808 1.000
2017-09-17 17:32:09,626 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCW67 1.000
2017-09-17 17:32:09,626 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCW68 1.000
2017-09-17 17:32:09,626 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC817 1.000
2017-09-17 17:32:09,627 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC818 1.000
2017-09-17 17:32:09,627 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC337 1.000
2017-09-17 17:32:09,627 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC338 1.000
2017-09-17 17:32:09,627 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCW65 1.000
2017-09-17 17:32:09,628 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCW66 1.000
2017-09-17 17:32:09,628 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC846 1.000
2017-09-17 17:32:09,628 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC847 1.000
2017-09-17 17:32:09,629 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBTA20 1.000
2017-09-17 17:32:09,629 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC848 1.000
2017-09-17 17:32:09,629 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC849 1.000
2017-09-17 17:32:09,630 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC850 1.000
2017-09-17 17:32:09,630 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCV61 1.000
2017-09-17 17:32:09,630 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCW60 1.000
2017-09-17 17:32:09,630 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCX70 1.000
2017-09-17 17:32:09,631 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCX78 1.000
2017-09-17 17:32:09,631 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCX79 1.000
2017-09-17 17:32:09,631 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBT6428 1.000
2017-09-17 17:32:09,631 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBT6429 1.000
2017-09-17 17:32:09,631 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC856 1.000
2017-09-17 17:32:09,632 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC857 1.000
2017-09-17 17:32:09,632 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC858 1.000
2017-09-17 17:32:09,632 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC859 1.000
2017-09-17 17:32:09,632 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BC860 1.000
2017-09-17 17:32:09,633 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBTA70 1.000
2017-09-17 17:32:09,633 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCV62 1.000
2017-09-17 17:32:09,633 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCW61 1.000
2017-09-17 17:32:09,633 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCX71 1.000
2017-09-17 17:32:09,634 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCX58 1.000
2017-09-17 17:32:09,634 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BCX59 1.000
2017-09-17 17:32:09,634 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBT5086 1.000
2017-09-17 17:32:09,634 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBT5087 1.000
2017-09-17 17:32:09,634 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BF517 1.000
2017-09-17 17:32:09,635 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 BFS17 1.000
2017-09-17 17:32:09,635 - PySpice.Spice.Parser.Element.__init__ - WARNING - QE   6 5 9 6 T1165A1
2017-09-17 17:32:09,635 - PySpice.Spice.Parser.Element.__init__ - WARNING - QA   6 9 7 6 T1165A2
2017-09-17 17:32:09,635 - PySpice.Spice.Parser.Element.__init__ - WARNING - QE   6 5 9 6 T1165A1
2017-09-17 17:32:09,635 - PySpice.Spice.Parser.Element.__init__ - WARNING - QA   6 9 7 6 T1165A2
2017-09-17 17:32:09,635 - PySpice.Spice.Parser.Element.__init__ - WARNING - QE   6 5 9 6 T1165A1
2017-09-17 17:32:09,636 - PySpice.Spice.Parser.Element.__init__ - WARNING - QA   6 9 7 6 T1165A2
2017-09-17 17:32:09,636 - PySpice.Spice.Parser.Element.__init__ - WARNING - QE   6 5 9 6 T1188A1
2017-09-17 17:32:09,636 - PySpice.Spice.Parser.Element.__init__ - WARNING - QA   6 9 7 6 T1188A2
2017-09-17 17:32:09,636 - PySpice.Spice.Parser.Element.__init__ - WARNING - QE   6 5 9 6 T1188A1
2017-09-17 17:32:09,636 - PySpice.Spice.Parser.Element.__init__ - WARNING - QA   6 9 7 6 T1188A2
2017-09-17 17:32:09,636 - PySpice.Spice.Parser.Element.__init__ - WARNING - QE   6 5 9 6 T1188A1
2017-09-17 17:32:09,636 - PySpice.Spice.Parser.Element.__init__ - WARNING - QA   6 9 7 6 T1188A2
2017-09-17 17:32:09,637 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBTA05 1.000
2017-09-17 17:32:09,637 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBTA06 1.000
2017-09-17 17:32:09,637 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBTA55 1.000
2017-09-17 17:32:09,637 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q    6 5 7 SMBTA56 1.000
2017-09-17 17:32:09,644 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q2 11 222 33 T0187 5.1
2017-09-17 17:32:09,645 - PySpice.Spice.Parser.Element.__init__ - WARNING - Q2 11 222 33 T0187 5.1
Quiescent Point 561.0 mV 0.3 mA
Quiescent Point 574.9 mV 0.4 mA
Quiescent Point 586.6 mV 0.5 mA
dynamic_resistance = ((quiescent_points[ 0]['quiescent_voltage'] -
                       quiescent_points[-1]['quiescent_voltage'])
                      /
                      (quiescent_points[ 0]['quiescent_current'] -
                       quiescent_points[-1]['quiescent_current']))

We found a dynamic resistance of 146.6 Ω.

We will now drive the diode with a sinusoidal source and perform an AC analysis.

../../_images/diode-characteristic-curve-circuit-ac.png
circuit = Circuit('Diode')
circuit.include(spice_library['BAV21'])
circuit.Sinusoidal('input', 'in', circuit.gnd,
                   dc_offset=dc_offset, offset=dc_offset,
                   amplitude=ac_amplitude)
R = circuit.R(1, 'in', 'out', 1@u_kΩ)
circuit.D('1', 'out', circuit.gnd, model='BAV21')

simulator = circuit.simulator(temperature=25, nominal_temperature=25)
analysis = simulator.ac(start_frequency=10@u_kHz, stop_frequency=1@u_GHz, number_of_points=10,  variation='dec')

Let plot the voltage across the diode and the dynamic resistance as a function of the frequency.

figure = plt.figure(1, (20, 10))

axe = plt.subplot(311)
# Fixme: handle unit in plot (scale and legend)
axe.semilogx(analysis.frequency, np.absolute(analysis.out)*1e3)
axe.grid(True)
axe.grid(True, which='minor')
axe.set_xlabel("Frequency [Hz]")
axe.set_ylabel("Vd [mV]")

axe = plt.subplot(312)
current = (analysis['in'] - analysis.out) / float(R.resistance)
axe.semilogx(analysis.frequency, np.absolute(analysis.out/current))
axe.grid(True)
axe.grid(True, which='minor')
axe.set_xlabel("Frequency [Hz]")
axe.set_ylabel('Rd [Ω]')

We will now drive the diode with a pulse generator and perform a transient analysis.

../../_images/diode-characteristic-curve-circuit-pulse.png
frequency = 1@u_MHz

circuit = Circuit('Diode')
circuit.include(spice_library['BAV21'])
# source = circuit.Sinusoidal('input', 'in', circuit.gnd,
#                             dc_offset=dc_offset, offset=dc_offset,
#                             amplitude=ac_amplitude,
#                             frequency=frequency)
source = circuit.Pulse('input', 'in', circuit.gnd,
                       initial_value=dc_offset-ac_amplitude, pulsed_value=dc_offset+ac_amplitude,
                       pulse_width=frequency.period/2, period=frequency.period)
circuit.R(1, 'in', 'out', 1@u_kΩ)
circuit.D('1', 'out', circuit.gnd, model='BAV21')

simulator = circuit.simulator(temperature=25, nominal_temperature=25)
analysis = simulator.transient(step_time=source.period/1e3, end_time=source.period*4)

axe = plt.subplot(313)
# Fixme: axis, x scale
# plot(analysis['in'] - dc_offset + quiescent_points[0]['quiescent_voltage'], axis=axe)
# plot(analysis.out, axis=axe)
axe.plot(analysis.out.abscissa*1e6, analysis.out)
axe.legend(('Vin [V]', 'Vout [V]'), loc=(.8,.8))
axe.grid()
axe.set_xlabel('t [μs]')
axe.set_ylabel('[V]')
# axe.set_ylim(.5, 1 + ac_amplitude + .1)

plt.tight_layout()
plt.show()
../../_images/diode-recovery-time.png

We notice the output of the circuit cannot follow the pulse generator. It is due to the capacitive behaviour of a PN junction that cut off the highest frequencies of the pulse. The plot of the dynamic resistance as a function of the frequency show a typical low pass filter behaviour where the impedance drop at high frequencies.