#r# ================ #r# This example show a CEM simulation. # Fixme: retrieve PDF reference and complete #################################################################################################### import os import matplotlib.pyplot as plt #################################################################################################### import PySpice.Logging.Logging as Logging logger = Logging.setup_logging() #################################################################################################### from PySpice.Doc.ExampleTools import find_libraries from PySpice.Probe.Plot import plot from PySpice.Spice.Library import SpiceLibrary from PySpice.Spice.Netlist import Circuit from PySpice.Unit import * #################################################################################################### libraries_path = find_libraries() spice_library = SpiceLibrary(libraries_path) ####################################################################################################
#!/usr/bin/env python3 #coding=utf-8 import sys import os import numpy, random, math import traceback import time, datetime ## Spice logger is disabled import PySpice.Logging.Logging as Logging logger = Logging.setup_logging(config_file="logging.yml") from PySpice.Spice.Netlist import Circuit from PySpice.Unit.Units import * import matplotlib.pyplot as plt try: import networkx except: print("Networkx library not installed. No graph will be produced") from PySpice.Spice.Library import SpiceLibrary from PySpice.Plot.BodeDiagram import bode_diagram from PySpice.Spice.Netlist import Circuit import deap from deap import base, tools, creator
# along with this program. If not, see <http://www.gnu.org/licenses/>. # #################################################################################################### #################################################################################################### import math import unittest import numpy as np from numpy import testing as np_test #################################################################################################### import PySpice.Logging.Logging as Logging logger = Logging.setup_logging() #################################################################################################### from PySpice.Probe.WaveForm import * from PySpice.Unit import * #################################################################################################### class TestUnits(unittest.TestCase): ############################################## @staticmethod def _test_unit_values(values, true_array):
def check_installation(self): """Tool to check PySpice is correctly installed. """ import ctypes.util try: print('Load PySpice module') import PySpice print('loaded {} version {}'.format(PySpice.__file__, PySpice.__version__)) except ModuleNotFoundError: print('PySpice module not found') return import PySpice.Logging.Logging as Logging logger = Logging.setup_logging(logging_level='INFO') from PySpice.Config import ConfigInstall from PySpice.Spice.NgSpice.Shared import NgSpiceShared ############################################## message = ''' NgSpiceShared configuration is NgSpiceShared.NGSPICE_PATH = {0.NGSPICE_PATH} NgSpiceShared.LIBRARY_PATH = {0.LIBRARY_PATH} ''' print(message.format(NgSpiceShared)) ############################################## if ConfigInstall.OS.on_windows: print('OS is Windows') library = NgSpiceShared.LIBRARY_PATH elif ConfigInstall.OS.on_osx: print('OS is OSX') library = 'ngspice' elif ConfigInstall.OS.on_linux: print('OS is Linux') library = 'ngspice' else: raise NotImplementedError library_path = ctypes.util.find_library(library) print('Found in library search path: {}'.format(library_path)) ############################################## print('\nLoad NgSpiceShared') ngspice = NgSpiceShared(verbose=True) if ConfigInstall.OS.on_linux: # For Linux see DLOPEN(3) # Apparently there is no simple way to get the path of the loaded library ... # But we can look in the process maps pid = os.getpid() maps_path = '/proc/{}/maps'.format(pid) with open(maps_path) as fh: for line in fh: if '.so' in line and 'ngspice' in line: parts = [x for x in line.split() if x] path = parts[-1] print('loaded {}'.format(path)) break message = ''' Ngspice version is {0.ngspice_version} has xspice: {0.has_xspice} has cider {0.has_cider} ''' print(message.format(ngspice)) command = 'version -f' print('> ' + command) print(ngspice.exec_command(command)) print() print('PySpice should work as expected')
def script(): logger = Logging.setup_logging() libraries_path = find_libraries() print(libraries_path) spice_library = SpiceLibrary(libraries_path) print(spice_library) circuit = Circuit('Diode Characteristic Curve') circuit.include(spice_library['1N4148']) print(spice_library['1N4148']) # def defaultCircuitParams(): circuit.V('input', 'in', circuit.gnd, 10 @ u_V) circuit.R(1, 'in', 'out', 1 @ u_Ω) # not required for simulation circuit.X('D1', '1N4148', 'out', circuit.gnd) # Voltage and Resistance in 10e-06, X is diode type # def setCircuitParams(V, R, X): # circuit.V('input', 'in', circuit.gnd, V@u_V) # circuit.R(1, 'in', 'out', R@u_Ω) # not required for simulation # circuit.X('D1', X, 'out', circuit.gnd) #r# We simulate the circuit at these temperatures: 0, 25 and 100 °C. # Fixme: Xyce ??? temperatures = [0, 25, 100] @ u_Degree analyses = {} # def simulateTemp(): for temperature in temperatures: simulator = circuit.simulator(temperature=temperature, nominal_temperature=temperature) analysis = simulator.dc(Vinput=slice(-2, 5, .01)) analyses[float(temperature)] = analysis #################################################################################################### #r# We plot the characteristic curve and compare it to the Shockley diode model: #r# #r# .. math:: #r# #r# I_d = I_s \left( e^{\frac{V_d}{n V_T}} - 1 \right) #r# #r# where :math:`V_T = \frac{k T}{q}` #r# #r# In order to scale the reverse biased region, we have to do some hack with Matplotlib. #r# silicon_forward_voltage_threshold = .7 shockley_diode = ShockleyDiode(Is=4e-9, degree=25) def two_scales_tick_formatter(value, position): if value >= 0: return '{} mA'.format(value) else: return '{} nA'.format(value / 100) formatter = ticker.FuncFormatter(two_scales_tick_formatter) figure, (ax1, ax2) = plt.subplots(2, figsize=(20, 10)) ax1.set_title('1N4148 Characteristic Curve ') ax1.set_xlabel('Voltage [V]') ax1.set_ylabel('Current') ax1.grid() ax1.set_xlim(-2, 2) ax1.axvspan(-2, 0, facecolor='green', alpha=.2) ax1.axvspan(0, silicon_forward_voltage_threshold, facecolor='blue', alpha=.1) ax1.axvspan(silicon_forward_voltage_threshold, 2, facecolor='blue', alpha=.2) ax1.set_ylim(-500, 750) # Fixme: round ax1.yaxis.set_major_formatter(formatter) Vd = analyses[25].out # compute scale for reverse and forward region forward_region = Vd >= 0 @ u_V reverse_region = np.invert(forward_region) scale = reverse_region * 1e11 + forward_region * 1e3 #?# check temperature for temperature in temperatures: analysis = analyses[float(temperature)] ax1.plot(Vd, -analysis.Vinput * scale) ax1.plot(Vd, shockley_diode.I(Vd) * scale, 'black') ax1.legend(['@ {} °C'.format(temperature) for temperature in temperatures] + ['Shockley Diode Model Is = 4 nA'], loc=(.02, .8)) ax1.axvline(x=0, color='black') ax1.axhline(y=0, color='black') ax1.axvline(x=silicon_forward_voltage_threshold, color='red') ax1.text(-1, -100, 'Reverse Biased Region', ha='center', va='center') ax1.text(1, -100, 'Forward Biased Region', ha='center', va='center') #r# Now we compute and plot the static and dynamic resistance. #r# #r# .. math:: #r# #r# \frac{d I_d}{d V_d} = \frac{1}{n V_T}(I_d + I_s) #r# #r# .. math:: #r# #r# r_d = \frac{d V_d}{d I_d} \approx \frac{n V_T}{I_d} ax2.set_title('Resistance @ 25 °C') ax2.grid() ax2.set_xlim(-2, 3) ax2.axvspan(-2, 0, facecolor='green', alpha=.2) ax2.axvspan(0, silicon_forward_voltage_threshold, facecolor='blue', alpha=.1) ax2.axvspan(silicon_forward_voltage_threshold, 3, facecolor='blue', alpha=.2) analysis = analyses[25] static_resistance = -analysis.out / analysis.Vinput dynamic_resistance = np.diff(-analysis.out) / np.diff(analysis.Vinput) ax2.semilogy(analysis.out, static_resistance, basey=10) ax2.semilogy(analysis.out[10:-1], dynamic_resistance[10:], basey=10) ax2.axvline(x=0, color='black') ax2.axvline(x=silicon_forward_voltage_threshold, color='red') ax2.axhline(y=1, color='red') ax2.text(-1.5, 1.1, 'R limitation = 1 Ω', color='red') ax2.legend(['{} Resistance'.format(x) for x in ('Static', 'Dynamic')], loc=(.05, .2)) ax2.set_xlabel('Voltage [V]') ax2.set_ylabel('Resistance [Ω]') plt.tight_layout() plt.show()
def define_circuit(self): logger = Logging.setup_logging() circuit_lab = self.circuit circuit = Circuit(circuit_lab["name"]) # for complex circuit elements that requires SPICE library # libraries_path = find_libraries() python_file = os.path.abspath(sys.argv[0]) examples_root = parent_directory_of(python_file) libraries_path = os.path.join(examples_root, 'libraries') spice_library = SpiceLibrary(libraries_path) # return message message = "" # add all elements to the PySpice circuit for element in circuit_lab: if element == "V": for dc_voltage_source in circuit_lab["V"]: circuit.V( dc_voltage_source["id"], circuit.gnd if dc_voltage_source["node1"] == "gnd" else dc_voltage_source["node1"], circuit.gnd if dc_voltage_source["node2"] == "gnd" else dc_voltage_source["node2"], dc_voltage_source["value"] @ u_V) elif element == "VA": for ac_voltage_source in circuit_lab["VA"]: circuit.SinusoidalVoltageSource( ac_voltage_source["id"], circuit.gnd if ac_voltage_source["node1"] == "gnd" else ac_voltage_source["node1"], circuit.gnd if ac_voltage_source["node2"] == "gnd" else ac_voltage_source["node2"], amplitude=ac_voltage_source["amplitude"] @ u_V, frequency=ac_voltage_source["frequency"] @ u_Hz, offset=ac_voltage_source["offset"] @ u_V) elif element == "I": for dc_current_source in circuit_lab["I"]: circuit.I( dc_current_source["id"], circuit.gnd if dc_current_source["node1"] == "gnd" else dc_current_source["node1"], circuit.gnd if dc_current_source["node2"] == "gnd" else dc_current_source["node2"], dc_current_source["value"] @ u_A) elif element == "IA": for ac_current_source in circuit_lab["IA"]: circuit.SinusoidalCurrentSource( ac_current_source["id"], circuit.gnd if ac_current_source["node1"] == "gnd" else ac_current_source["node1"], circuit.gnd if ac_current_source["node2"] == "gnd" else ac_current_source["node2"], amplitude=ac_current_source["amplitude"] @ u_A, frequency=ac_current_source["frequency"] @ u_Hz, offset=ac_current_source["offset"] @ u_A) elif element == "R": for resistor in circuit_lab["R"]: circuit.R( resistor["id"], circuit.gnd if resistor["node1"] == "gnd" else resistor["node1"], circuit.gnd if resistor["node2"] == "gnd" else resistor["node2"], resistor["value"] @ u_Ω) elif element == "L": for inductor in circuit_lab["L"]: circuit.L( inductor["id"], circuit.gnd if inductor["node1"] == "gnd" else inductor["node1"], circuit.gnd if inductor["node2"] == "gnd" else inductor["node2"], inductor["value"] @ u_H) elif element == "C": for capacitor in circuit_lab["C"]: circuit.C( capacitor["id"], circuit.gnd if capacitor["node1"] == "gnd" else capacitor["node1"], circuit.gnd if capacitor["node2"] == "gnd" else capacitor["node2"], capacitor["value"] @ u_F) elif element == "D": for diode in circuit_lab["D"]: try: circuit.include(spice_library[diode["modelType"]]) circuit.X( diode["id"], diode["modelType"], circuit.gnd if diode["node1"] == "gnd" else diode["node1"], circuit.gnd if diode["node2"] == "gnd" else diode["node2"]) except KeyError as e: message += " " + str(e) elif element == "nBJT": for nBJT in circuit_lab["nBJT"]: try: circuit.include(spice_library[nBJT["modelType"]]) circuit.BJT(nBJT["id"], circuit.gnd if nBJT["node1"] == "gnd" else nBJT["node1"], circuit.gnd if nBJT["node2"] == "gnd" else nBJT["node2"], circuit.gnd if nBJT["node3"] == "gnd" else nBJT["node3"], model=nBJT["modelType"]) except KeyError as e: message += " " + str(e) elif element == "pBJT": for pBJT in circuit_lab["pBJT"]: try: circuit.include(spice_library[pBJT["modelType"]]) circuit.BJT(pBJT["id"], circuit.gnd if pBJT["node3"] == "gnd" else pBJT["node3"], circuit.gnd if pBJT["node2"] == "gnd" else pBJT["node2"], circuit.gnd if pBJT["node1"] == "gnd" else pBJT["node1"], model=pBJT["modelType"]) except KeyError as e: message += " " + str(e) elif element == "NMOS": for NMOS in circuit_lab["NMOS"]: try: circuit.include(spice_library[NMOS["modelType"]]) # nodes are: drain, gate, source, bulk circuit.MOSFET(NMOS["id"], circuit.gnd if NMOS["node4"] == "gnd" else NMOS["node4"], circuit.gnd if NMOS["node2"] == "gnd" else NMOS["node2"], circuit.gnd if NMOS["node3"] == "gnd" else NMOS["node3"], circuit.gnd if NMOS["node1"] == "gnd" else NMOS["node1"], model=NMOS["modelType"]) except KeyError as e: message += " " + str(e) elif element == "PMOS": for PMOS in circuit_lab["PMOS"]: try: circuit.include(spice_library[PMOS["modelType"]]) # nodes are: source, gate, drain, bulk circuit.MOSFET(PMOS["id"], circuit.gnd if PMOS["node1"] == "gnd" else PMOS["node1"], circuit.gnd if PMOS["node2"] == "gnd" else PMOS["node2"], circuit.gnd if PMOS["node3"] == "gnd" else PMOS["node3"], circuit.gnd if PMOS["node4"] == "gnd" else PMOS["node4"], model=PMOS["modelType"]) except KeyError as e: message += " " + str(e) # add ammeter as a 0 volt voltage source elif element == "AM": for ammeter in circuit_lab["AM"]: circuit.V( ammeter["id"], circuit.gnd if ammeter["node1"] == "gnd" else ammeter["node1"], circuit.gnd if ammeter["node2"] == "gnd" else ammeter["node2"], ammeter["value"] @ u_V) if not message: self.spice = circuit return message return "Undefined model type:" + message