Subtracting OpAmp - Unclear Documentation/Examples.
Follows from this mattermost thread.
The documentation/examples for OpAmps are unclear about how to use both ports, for example this subtracting arrangement. Below is a minimum working example, with some inspiration from the CIT 40m fork of Zero.
import numpy as np
from zero import Circuit
from zero.components import OpAmp
from zero.analysis import AcSignalAnalysis
opa188 = {# Part name.
'model' : 'OPA188',
# Open-loop gain: USE HIGH VOLTAGE - Supply.
'a0' : 10**(136/20),
# Gain bandwidth product.
'gbw' : 2e6,
# Flat voltage noise level - spectral density.
'vnoise' : 8.8e-9,
# Corner for voltage noise, in Hz - extrapolation.
'vcorner' : 65e-3,
# Flat current noise level.
'inoise' : 7e-15,
# Corner for current noise, in Hz - guess.
'icorner' : 65e-3,
# Maximum voltage output.
'vmax' : 15,
# Maximum current output.
'imax' : 16e-3,
# Slew rate
'sr' : 0.8e6}
# Non-inverting amplifier.
non_invert_amp = Circuit()
# Around non-inverting input.
# Input to non-inverting port.
non_invert_amp.add_resistor(name = 'R2', value = 1e3, node1 = 'Vin',
node2 = 'Vp')
# Non-inverting port to ground.
non_invert_amp.add_resistor(name = 'R4', value = 6e3, node1 = 'Vp',
node2 = 'gnd')
non_invert_amp.add_capacitor(name = 'C2', value = 1.5e-6, node1 = 'Vp',
node2 = 'gnd')
# Around inverting input.
# Inverting input to ground.
non_invert_amp.add_resistor(name = 'R1', value = 1e3, node1 = 'gnd',
node2 = 'Vn')
# Inverting input to output.
non_invert_amp.add_resistor(name = 'R3', value = 6e3, node1 = 'Vn',
node2 = 'Vo')
non_invert_amp.add_capacitor(name = 'C1', value = 1.5e-6, node1 = 'Vn',
node2 = 'Vo')
# OpAmp.
non_invert_amp.add_component(OpAmp(name = 'U1', node1 = 'Vp', node2 = 'Vn',
node3 = 'Vo', **opa188))
# Output LPF.
non_invert_amp.add_resistor(name = 'R5', value = 5e3, node1 = 'Vo',
node2 = 'Vout')
non_invert_amp.add_capacitor(name = 'C3', value = 2e-6, node1 = 'Vout',
node2 = 'gnd')
# Analysis.
# Frequency vector.
f = np.geomspace(30e-3, 3e2, num = 401)
# Setup analysis?
analyse_NonInvert = AcSignalAnalysis(circuit = non_invert_amp)
# Do analysis.
tf_NonInvert = analyse_NonInvert.calculate(frequencies = f,
node = 'Vin', input_type = 'voltage')
# Subtracting amplifier.
subtract_amp = Circuit()
# Around non-inverting input.
# Input to non-inverting port.
subtract_amp.add_resistor(name = 'R2', value = 1e3, node1 = 'VinP',
node2 = 'Vp')
# Non-inverting port to ground.
subtract_amp.add_resistor(name = 'R4', value = 6e3, node1 = 'Vp',
node2 = 'gnd')
subtract_amp.add_capacitor(name = 'C2', value = 1.5e-6, node1 = 'Vp',
node2 = 'gnd')
# Around inverting input.
# Inverting input to ground.
subtract_amp.add_resistor(name = 'R1', value = 1e3, node1 = 'VinN',
node2 = 'Vn')
# Inverting input to output.
subtract_amp.add_resistor(name = 'R3', value = 6e3, node1 = 'Vn',
node2 = 'Vo')
subtract_amp.add_capacitor(name = 'C1', value = 1.5e-6, node1 = 'Vn',
node2 = 'Vo')
# OpAmp.
subtract_amp.add_component(OpAmp(name = 'U1', node1 = 'Vp', node2 = 'Vn',
node3 = 'Vo', **opa188))
# Output LPF.
subtract_amp.add_resistor(name = 'R5', value = 5e3, node1 = 'Vo',
node2 = 'Vout')
subtract_amp.add_capacitor(name = 'C3', value = 2e-6, node1 = 'Vout',
node2 = 'gnd')
# Analysis.
# Setup
analyse_Subtractor = AcSignalAnalysis(circuit = subtract_amp)
# Analyse.
tf_Subtractor = analyse_Subtractor.calculate(frequencies = f,
node = 'VinP', input_type = 'voltage')
The non-inverting OpAmp tf_NonInvert.plot_response(sink='Vout').show()
, and subtracting OpAmp tf_Subtractor.plot_response(sink='Vout').show()
transfer functions should be identical but aren't: 15.5 dB
vs -1 dB
respectively.