def main(): # Monkeypatch the exec_command() to not bail if it detects an 'error' in stderr, # these 'errors' don't seem to actually prevent the sim from working correctly Shared.NgSpiceShared.exec_command = exec_command libraries_path = find_libraries() spice_library = SpiceLibrary(libraries_path) directory_path = Path(__file__).resolve().parent kicad_netlist_path = directory_path.joinpath('simulation.cir') netlist_without_quotes_path = Path('./simulation-without-quotes.cir') remove_include_quotes(kicad_netlist_path, netlist_without_quotes_path) parser = SpiceParser(path=str(netlist_without_quotes_path)) circuit = parser.build_circuit(ground=0) simulator = circuit.simulator(temperature=25, nominal_temperature=25) analysis = simulator.transient(step_time=100 @ u_ps, end_time=100 @ u_ns) v_out = analysis['OUT'] fig, ax = plt.subplots(1, figsize=(10, 5)) ax.plot(v_out.abscissa.as_ndarray() * 1e9, v_out.as_ndarray()) ax.legend(('OUT', ), loc=(.8, .8)) ax.grid() ax.set_xlabel('Time (ns)') ax.set_ylabel('Voltage (V)') plt.tight_layout() plt.savefig('out.png')
def main(): # Monkeypatch the exec_command() to not bail if it detects an 'error' in stderr, # these 'errors' don't seem to actually prevent the sim from working correctly Shared.NgSpiceShared.exec_command = exec_command libraries_path = find_libraries() spice_library = SpiceLibrary(libraries_path) directory_path = Path(__file__).resolve().parent kicad_netlist_path = directory_path.joinpath( 'wien-bridge-oscillator-sim.cir') parser = SpiceParser(path=str(kicad_netlist_path)) circuit = parser.build_circuit(ground=0) simulator = circuit.simulator(temperature=25, nominal_temperature=25) analysis = simulator.transient(step_time=10 @ u_us, end_time=60 @ u_ms) v_sine_out = analysis['SINE_OUT'] first_index_of_interest = np.where( v_sine_out.abscissa.as_ndarray() > 0.04)[0][0] fig, ax = plt.subplots(1, figsize=(10, 5)) ax.plot(v_sine_out.abscissa.as_ndarray()[first_index_of_interest:], v_sine_out.as_ndarray()[first_index_of_interest:]) ax.legend(('SINE_OUT', ), loc=(.8, .8)) ax.grid() ax.set_xlabel('Time (s)') ax.set_ylabel('Voltage (V)') plt.tight_layout() plt.savefig('v-sine-out.png')
def __init__(self, name): Circuit.__init__(self, name) libraries_path = 'C:\\Users\\micha\\PycharmProjects\\circuit_model\\component_libraries\\transistor' spice_library = SpiceLibrary(libraries_path) self.include(spice_library['ptm65nm_nmos']) self.include(spice_library['ptm65nm_pmos']) # input pair self.MOSFET(1, "vd_l", "inp", "vtail", self.gnd, model='ptm65nm_nmos', width=5e-6, length=0.65e-6) self.MOSFET(2, "vd_r", "inn", "vtail", self.gnd, model='ptm65nm_nmos', width=5e-6, length=0.65e-6) # active load self.MOSFET(3, "vd_l", "vd_l", "vdd", "vdd", model='ptm65nm_pmos', width=2.5e-6, length=0.65e-6) self.MOSFET(4, "vd_r", "vd_l", "vdd", "vdd", model='ptm65nm_pmos', width=2.5e-6, length=0.65e-6) # tail current self.MOSFET(5, "vtail", "vbias", self.gnd, self.gnd, model='ptm65nm_nmos', width=5e-6, length=1e-6) # input current mirror self.MOSFET(6, "vbias", "vbias", self.gnd, self.gnd, model='ptm65nm_nmos', width=5e-6, length=1e-6) # sources self.I(1, "vdd", "vbias", 1e-6) # load self.R('load', 'vd_r', self.gnd, 1e9) self.C('load', 'vd_r', self.gnd, 5e-12)
def test_buck_converter(): spice_library = SpiceLibrary(os.path.abspath(os.path.dirname(__file__))) spice_library['1N4148'] Vin = 20 @ u_V Vout = 15 @ u_V Vpulse = 5 @ u_V ratio = Vout / Vin clk_freq = 1 @ u_MHz clk_period = clk_freq.period sw_freq = 50 @ u_kHz sw_period = sw_freq.period sw_period_cnt = int(sw_period / clk_period) sw_width_cnt = int(sw_period_cnt * (1 - ratio)) w_period = bitw(sw_period_cnt) def buck_converter(c, ins, outs): c.include(spice_library['1N4148']) ins.append(('gate', c.gnd)) outs.append(('output', c.gnd)) Rload = 3 @ u_Ohm L = 750 @ u_uH Cout = 0.47 @ u_uF c.V('input', 'vin', c.gnd, Vin) c.VCS(name='sw1', input_plus='gate', input_minus=c.gnd, output_minus='vin', output_plus='sw_out', model='SW', initial_state='off') c.model('SW', 'SW', Ron=1 @ u_mOhm, Roff=100 @ u_MOhm, Vt=1.1 @ u_V) c.X('D1', '1N4148', c.gnd, 'sw_out') c.L(1, 'sw_out', 'output', L) c.R(1, 'output', c.gnd, Rload) c.C(1, 'output', c.gnd, Cout) seq = [(sw_period_cnt, sw_width_cnt)] * 1000 din = drv(t=Tuple[Uint[w_period], Uint[w_period]], seq=seq) \ | pulse \ | flatten \ | Float din = din * int(Vpulse) dout = din | ngspice(f=buck_converter, init_x=0) dout | scope dout | shred verilate('/pulse') sim(timeout=4000, check_activity=False)
def test_half_rect(): spice_library = SpiceLibrary(os.path.abspath(os.path.dirname(__file__))) spice_library['1N4148'] def half_rect(c, ins, outs): c.include(spice_library['1N4148']) ins.append(('in', c.gnd)) outs.append(('output', c.gnd)) c.X('D1', '1N4148', 'in', 'output') c.R('load', 'output', c.gnd, 100) c.C('1', 'output', c.gnd, 1e-3) def sinus(a, w, th, t_step): t = 0 while True: yield a * math.sin(w * t + th) t += t_step clk_freq = 1e6 drv(t=Float, seq=sinus(a=10, w=2*math.pi*50, th=0, t_step=1 / clk_freq)) \ | ngspice(f=half_rect, init_x=0) \ | scope sim(timeout=40000)
def create_circuit(genome, config): libraries_path = '/home/alan/ngspice/libraries' # os.path.join(os.path.dirname(os.path.dirname(__file__)), 'libraries') spice_library = SpiceLibrary(libraries_path) circuit = Circuit('NEAT') circuit.include(spice_library['1N4148']) Vbase = circuit.V('base', 'input1', circuit.gnd, 2) Vcc = circuit.V('cc', 'input2', circuit.gnd, 5) Vgnd = circuit.V('gnd', 'input3', circuit.gnd, 0) #circuit.R('test1', 'node0', circuit.gnd, 1e6) #circuit.R('test2', 'node0', 'input1', 1e6) ridx = 1 xidx = 1 for key, c in iteritems(genome.connections): if c.component == 'resistor': pin0, pin1 = get_pins(key) R = 10**c.value circuit.R(ridx, pin1, pin0, R) ridx += 1 elif c.component == 'diode': pin0, pin1 = get_pins(key) circuit.X(xidx, '1N4148', pin1, pin0) xidx += 1 return circuit
def init(): logger = Logging.setup_logging() # libraries_path = find_libraries() libraries_path = '/home/anurag/Workspace/DTU/CourseProj/AE/website/DTU-VLAB/dtuvlab/lab/simulations/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']) return circuit
def main(): # Monkeypatch the exec_command() to not bail if it detects an 'error' in stderr, # these 'errors' don't seem to actually prevent the sim from working correctly Shared.NgSpiceShared.exec_command = exec_command libraries_path = find_libraries() spice_library = SpiceLibrary(libraries_path) directory_path = Path(__file__).resolve().parent kicad_netlist_path = directory_path.joinpath('schematic.cir') netlist_without_quotes_path = Path('./simulation-without-quotes.cir') remove_include_quotes(kicad_netlist_path, netlist_without_quotes_path) parser = SpiceParser(path=str(netlist_without_quotes_path)) circuit = parser.build_circuit(ground=0) simulator = circuit.simulator(temperature=25, nominal_temperature=25) # analysis = simulator.transient(step_time=100@u_us, end_time=1000@u_ms) analysis = simulator.ac(start_frequency=100@u_mHz, stop_frequency=10@u_kHz, number_of_points=50, variation='dec') v_out = analysis['v_out'] mag = 20*np.log10(np.abs(v_out.as_ndarray())) print(mag) freq = analysis.frequency.as_ndarray() r1_Ohms = 2e3 r2_Ohms = 10e3 ratio = r2_Ohms / (r1_Ohms + r2_Ohms) ratio_dB = 20*np.log10(ratio) ratio_dB = -1.75 r_parallel = (r1_Ohms * r2_Ohms) / (r1_Ohms + r2_Ohms) c_F = 10e-6 fc = 1 / (2*np.pi*r_parallel*c_F) print(ratio_dB) fig, ax = plt.subplots(1, figsize=(10, 7)) ax.plot(freq, mag, label='$V_{OUT}$') # ax.legend(('$V_{OUT}$',), loc=(.8,.8)) ax.grid() ax.set_xscale('log') ax.set_xlabel('Frequency (Hz)') ax.set_ylabel('Voltage Magnitude (dB)') ax.axvline(fc, color='C2', label='$f_c$') ax.axhline(ratio_dB, color='C1', label=f'{ratio_dB:.2f}dB (DC gain)') ax.axhline(ratio_dB - 3.0, color='C1', label=f'{ratio_dB - 3:.2f}dB (DC gain - 3dB)') ax.legend() plt.tight_layout() plt.savefig('out.png')
def main(): # Monkeypatch the exec_command() to not bail if it detects an 'error' in stderr, # these 'errors' don't seem to actually prevent the sim from working correctly Shared.NgSpiceShared.exec_command = exec_command libraries_path = find_libraries() spice_library = SpiceLibrary(libraries_path) directory_path = Path(__file__).resolve().parent kicad_netlist_path = directory_path.joinpath('schematic.cir') netlist_without_quotes_path = Path('./simulation-without-quotes.cir') remove_include_quotes(kicad_netlist_path, netlist_without_quotes_path) parser = SpiceParser(path=str(netlist_without_quotes_path)) circuit = parser.build_circuit(ground=0) simulator = circuit.simulator(temperature=25, nominal_temperature=25) analysis = simulator.transient(step_time=1 @ u_us, end_time=1000 @ u_us) # analysis = simulator.ac(start_frequency=100@u_mHz, stop_frequency=10@u_kHz, number_of_points=50, variation='dec') v_in = analysis['/v_in'] v_out = analysis['v_out'] fig, ax = plt.subplots(1, figsize=(7, 5)) ax.plot(v_in.abscissa.as_ndarray() * 1e6, v_in.as_ndarray(), label='$v_{in}$') ax.plot(v_out.abscissa.as_ndarray() * 1e6, v_out.as_ndarray(), label='$v_{out}$') ax.axhline(-5.0, ls='--', color='C0') ax.axhline(5.0, ls='--', color='C0') ax.axhline(0, ls='--', color='C1') ax.axhline(3.3, ls='--', color='C1') ax.grid() ax.set_xlabel('Time [us]') ax.set_ylabel('Voltage [V]') ax.legend() plt.tight_layout() plt.savefig('out.png')
from PySpice.Spice.Library import SpiceLibrary spice_library = SpiceLibrary('../examples/libraries') # print list(spice_library.iter_on_subcircuits()) # print list(spice_library.iter_on_models()) print(list(spice_library.subcircuits)) print(list(spice_library.models))
Rh = 0.5 * 7.4 @ u_Ω # Internal hub resistance, halved to compensate for 10 years # of innovation. Rhp = 2 * 76.6 @ u_Ω # Internal hub resistance (parasitic), doubled to compensate # for 10 years of innovation. # Load model Pl = -1 @ u_W # constant power sink value. Use -1W for one red LED at full power. C = 200 @ u_uF # input capacitor # You need to clone the PySpice git repo somewhere and set the # PYSPICE_EXAMPLES_PATH environment variable to the examples subdirectory. # TODO: just import a diode model for the diodes we actually intend on using, # e.g. schottky. pyspice_examples_libraries_path = os.path.join( os.environ['PYSPICE_EXAMPLES_PATH'], 'libraries') pyspice_examples_spice_library = SpiceLibrary(pyspice_examples_libraries_path) # My own custom spice libraries, already next to this script. custom_libraries_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'libraries') custom_spice_library = SpiceLibrary(custom_libraries_path) circuit = Circuit('dynamo hub LED driver') circuit.include(pyspice_examples_spice_library['1N4148']) # standard diode circuit.include(custom_spice_library['CONSTANT_POWER_LOAD'] ) # models a switching regulator # Hub dynamo model source = circuit.Sinusoidal('input', 'in', circuit.gnd,
def _gen_netlist_(self, **kwargs): """ Return a PySpice Circuit generated from a SKiDL circuit. Args: title: String containing the title for the PySpice circuit. libs: String or list of strings containing the paths to directories containing SPICE models. """ if USING_PYTHON2: return None # Create an empty PySpice circuit. title = kwargs.pop('title', '') # Get title and remove it from kwargs. circuit = PySpiceCircuit(title) # Initialize set of libraries to include in the PySpice circuit. includes = set() # Add any models used by the parts. models = set([getattr(part, 'model', None) for part in self.parts]) models.discard(None) lib_dirs = set(flatten([kwargs.get('libs', None)])) lib_dirs.discard(None) spice_libs = [SpiceLibrary(dir) for dir in lib_dirs] for model in models: for spice_lib in spice_libs: try: includes.add(spice_lib[model]) except KeyError: pass else: break else: logger.error('Unknown SPICE model: {}'.format(model)) # Add any subckt libraries used by the parts. part_names = set([ getattr(part, 'name', None) for part in self.parts if getattr(part, 'filename', None) ]) lib_files = set([getattr(part, 'filename', None) for part in self.parts]) lib_files.discard(None) lib_dirs = [os.path.dirname(f) for f in lib_files] spice_libs = [SpiceLibrary(dir) for dir in lib_dirs] for part_name in part_names: for spice_lib in spice_libs: try: includes.add(spice_lib[part_name]) except KeyError: pass else: break else: logger.error('Unknown SPICE subckt: {}'.format(part_name)) # Add the models and subckt libraries to the PySpice circuit. for inc in includes: circuit.include(inc) # Add each part in the SKiDL circuit to the PySpice circuit. for part in sorted(self.parts, key=lambda p: str(p.ref)): # Add each part. All PySpice parts have an add_to_spice attribute # and can be added directly. Other parts are added as subcircuits. try: add_func = part.pyspice['add'] except (AttributeError, KeyError): logger.error('Part has no SPICE model: {}'.format(part)) else: add_func(part, circuit) return circuit
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.Spice.Parser import SpiceParser from PySpice.Spice.Netlist import SubCircuitFactory from PySpice.Unit import * #################################################################################################### # IMPORT SPICE NETLIST OF CIRCUIT: spice_library = SpiceLibrary('../libraries/') netlist_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'NAND2.sp') parser = SpiceParser(path=netlist_path) circuit = parser.build_circuit() circuit.include(spice_library['NAND2']) #################################################################################################### # CONFIGURE VOLTAGE SOURCES supply = 1@u_V circuit.V('supply', 'vdd', 'vss', supply) circuit.V('ground', 'vss', circuit.gnd, circuit.gnd) ####################################################################################################
def _load_sch_lib_(self, filename=None, lib_search_paths_=None, lib_section=None): """ Load the .subckt I/O from a SPICE library file. Args: filename: The name of the SPICE library file. lib_search_paths_ : List of directories to search for the file. """ from ..skidl import lib_suffixes from ..part import Part from ..pin import Pin if os.path.isdir(filename): # A directory was given, so just use that. spice_lib_path = os.path.abspath(filename) else: # A file name was given, so find the absolute file path in the search paths. fp, spice_lib_path = find_and_open_file( filename=filename, paths=lib_search_paths_, ext=lib_suffixes[SPICE], exclude_binary=True, descend=-1, ) fp.close() # Close the file pointer. We just need the path to the file. # Read the Spice library from the given path. spice_lib = SpiceLibrary( root_path=spice_lib_path, recurse=True, section=lib_section ) # Get the unique set of files referenced by the subcircuits in the Spice library. lib_files = set([str(spice_lib[subcirc]) for subcirc in spice_lib.subcircuits]) # Go through the files and create a SKiDL Part for each subcircuit. for lib_file in lib_files: with open(lib_file) as f: # Read the definition of each part line-by-line and then create # a Part object that gets stored in the part list. for statement in _gather_statement(f): # Look for the start of a part definition. if statement[0] == ".subckt": # Create an un-filled part template. part = Part(part_defn="don't care", tool=SPICE, dest=LIBRARY) part.fplist = [] part.aliases = [] part.num_units = 1 part.ref_prefix = "X" part._ref = None part.filename = "" part.name = "" part.pins = [] part.pyspice = { "name": "X", "add": add_subcircuit_to_circuit, "lib": spice_lib, "lib_path": spice_lib_path, "lib_section": lib_section, } # Flesh-out the part. # Parse the part definition. pieces = statement try: # part defn: .subckt part_name pin1, pin2, ... pinN. part.name = pieces[1] part.pins = [Pin(num=p, name=p) for p in pieces[2:]] part.associate_pins() except IndexError: logger.warn( "Misformatted SPICE subcircuit: {}".format(part.part_defn) ) else: # Now find a symbol file for the part to assign names to the pins. # First, check for LTSpice symbol file. sym_file, sym_file_path = find_and_open_file( part.name, lib_search_paths_, ".asy", allow_failure=True, exclude_binary=True, descend=-1, ) if sym_file: pin_names = [] pin_indices = [] for sym_line in sym_file: if not sym_line: continue if sym_line.lower().startswith("pinattr pinname"): pin_names.append(sym_line.split()[2]) elif sym_line.lower().startswith("pinattr spiceorder"): pin_indices.append(sym_line.split()[2]) elif sym_line.lower().startswith("symattr description"): part.description = " ".join(sym_line.split()[2:]) sym_file.close() # Pin names and indices should be matched by the order they # appeared in the symbol file. Each index should match the # order of the pins in the .subckt file. for index, name in zip(pin_indices, pin_names): part.pins[int(index) - 1].name = name else: # No LTSpice symbol file, so check for PSPICE symbol file. sym_file, sym_file_path = find_and_open_file( filename, lib_search_paths_, ".slb", allow_failure=True, exclude_binary=True, descend=-1, ) if sym_file: pin_names = [] active = False for sym_line in sym_file: sym_line = sym_line.strip() if not sym_line: continue line_parts = sym_line.lower().split() if line_parts[0] == "*symbol": active = line_parts[1] == part.name.lower() if active: if line_parts[0] == "p": pin_names.append(line_parts[6]) elif line_parts[0] == "d": part.description = " ".join(line_parts[1:]) sym_file.close() pin_indices = list(range(len(pin_names))) for pin, name in zip(part.pins, pin_names): pin.name = name # Add subcircuit part to the library. self.add_parts(part)
def _gen_netlist_(self, **kwargs): """ Return a PySpice Circuit generated from a SKiDL circuit. Args: title: String containing the title for the PySpice circuit. libs: String or list of strings containing the paths to directories containing SPICE models. """ from ..skidl import lib_search_paths if USING_PYTHON2: return None # Replace any special chars in all net names because Spice won't like them. # Don't use self.get_nets() because that only returns a single net from a # group of attached nets so the other nets won't get renamed. for net in self.nets: net.replace_spec_chars_in_name() # Create an empty PySpice circuit. title = kwargs.pop("title", "") # Get title and remove it from kwargs. circuit = PySpiceCircuit(title) # Default SPICE libraries will be read-in down below if needed. default_libs = [] # Initialize set of libraries to include in the PySpice circuit. model_paths = set() # Paths to the model files that have been used. lib_paths = set() # Paths to the library files that have been used. lib_ids = set() # A lib_id is a tuple of the path to the lib file and a section. for part in self.parts: try: pyspice = part.pyspice except AttributeError: continue model = getattr(part, "model", None) if model: if isinstance(model, (XspiceModel, DeviceModel)): circuit.model(*model.args, **model.kwargs) else: try: path = pyspice["lib"][model] except KeyError: # The part doesn't contain the library with the model, so look elsewhere. if not default_libs: # Read the default SPICE libraries. for path in lib_search_paths[SPICE]: default_libs.append( SpiceLibrary(root_path=path, recurse=True) ) # Search for the model in the default libraries. path = None for lib in default_libs: try: path = lib[model] break except KeyError: pass if path == None: logger.error( "Unable to find model {} for part {}".format( model, part.ref ) ) # Include the model file if it hasn't been included yet. if path != None and path not in model_paths: circuit.include(path) model_paths.add(path) try: path, section = pyspice["lib_path"], pyspice["lib_section"] except KeyError: continue if not section: # Libraries without a section are added as include files. if path not in lib_paths: circuit.include(path) lib_paths.add(path) else: lib_id = (path, section) if lib_id not in lib_ids: circuit.lib(*lib_id) lib_ids.add(lib_id) # Add each part in the SKiDL circuit to the PySpice circuit. # TODO: Make sure self.parts is processed in order that parts were created so ngspice doesn't get references to parts before they exist. for part in self.parts: # Add each part using its add function which will be either # add_part_to_circuit() or add_subcircuit_to_circuit(). try: add_func = part.pyspice["add"] except (AttributeError, KeyError): logger.error("Part has no SPICE model: {}".format(part)) else: add_func(part, circuit) return circuit
def test_pyspice_envelope(): times, dt = np.linspace(0, 100e-9, 2048, endpoint=False, retstep=True) pulse = pyrex.AskaryanSignal(times=times, energy=1e8, theta=45*np.pi/180, n=1.75, t0=20e-9) performance_test("pyrex.Signal(pulse.times, pulse.envelope)", number=1000, setup="import pyrex", use_globals={"pulse": pulse}) spice_library = SpiceLibrary("/Users/bhokansonfasig/Documents/IceCube/"+ "scalable_radio_array/spice_models") class NgSpiceSharedSignal(NgSpiceShared): def __init__(self, **kwargs): super().__init__(**kwargs) self._signal = None def get_vsrc_data(self, voltage, time, node, ngspice_id): self._logger.debug('ngspice_id-{} get_vsrc_data @{} node {}'.format(ngspice_id, time, node)) voltage[0] = np.interp(time, self._signal.times, self._signal.values) return 0 ngspice_shared = NgSpiceSharedSignal() class NgSpiceSignal: def __init__(self, signal, shared=ngspice_shared): self.shared = ngspice_shared self.shared._signal = signal def setup_circuit(kind="biased"): if kind=="biased": circuit = Circuit('Biased Envelope Circuit') circuit.include(spice_library['hsms']) circuit.V('in', 'input', circuit.gnd, 'dc 0 external') # bias portion circuit.C(2, 'input', 1, 10@u_nF) circuit.R(2, 1, 2, 1@u_kOhm) circuit.X('D2', 'hsms', 2, circuit.gnd) circuit.R(3, 2, 'bias', 1@u_kOhm) circuit.V('bias', 'bias', circuit.gnd, 5@u_V) # envelope portion circuit.X('D1', 'hsms', 1, 'output') circuit.C(1, 'output', circuit.gnd, 220@u_pF) circuit.R(1, 'output', circuit.gnd, 50@u_Ohm) return circuit elif kind=="basic": circuit = Circuit('Biased Envelope Circuit') circuit.include(spice_library['hsms']) circuit.V('in', 'input', circuit.gnd, 'dc 0 external') # envelope portion circuit.X('D1', 'hsms', 'input', 'output') circuit.C(1, 'output', circuit.gnd, 220@u_pF) circuit.R(1, 'output', circuit.gnd, 50@u_Ohm) return circuit elif kind=="diode": circuit = Circuit('Diode Output') circuit.include(spice_library['hsms']) circuit.V('in', 'input', circuit.gnd, 'dc 0 external') circuit.X('D1', 'hsms', 'input', 'output') return circuit performance_test("ng_in = NgSpiceSignal(pulse); "+ "simulator = circuit.simulator(temperature=25, "+ "nominal_temperature=25, ngspice_shared=ng_in.shared); "+ "analysis = simulator.transient(step_time=dt, "+ "end_time=pulse.times[-1]); "+ "pyrex.Signal(analysis.output.abscissa, analysis.output)", number=10, setup="import pyrex; circuit = setup_circuit()", use_globals={"pulse": pulse, "dt": dt, "setup_circuit": setup_circuit, "NgSpiceSignal": NgSpiceSignal}, alternate_title="pyrex.Signal(analysis.output.abscissa, "+ "analysis.output)") def envelope_circuit(signal, cap=220e-12, res=50): v_c = 0 v_out = [] r_d = 25 i_s = 3e-6 n = 1.06 v_t = 26e-3 charge_exp = np.exp(-signal.dt/(res*cap)) discharge = i_s*res*(1-charge_exp) lambert_factor = n*v_t*res/r_d*(1-charge_exp) frac = i_s*r_d/n/v_t lambert_exponent = np.log(frac) + frac for v_in in signal.values: a = lambert_exponent + (v_in - v_c)/n/v_t if a>100: b = np.log(a) lambert_term = a - b + b/a else: lambert_term = np.real(lambertw(np.exp(a))) if np.isnan(lambert_term): lambert_term = 0 v_c = v_c*charge_exp - discharge + lambert_factor*lambert_term v_out.append(v_c) return pyrex.Signal(signal.times, v_out, value_type=pyrex.Signal.ValueTypes.voltage) performance_test("envelope(pulse)", number=100, use_globals={"pulse": pulse, "envelope": envelope_circuit}, alternate_title="analytic circuit simulation")
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 * # ===== Create circuit ===== spice_library = SpiceLibrary("libraries") circuit = Circuit('Transistor') Vbase = circuit.V('base', '1', circuit.gnd, 1@u_V) circuit.R('base', 1, 'base', 1@u_kΩ) Vcollector = circuit.V('collector', '2', circuit.gnd, 0@u_V) circuit.R('collector', 2, 'collector', 1@u_kΩ) # circuit.BJT(1, 'collector', 'base', circuit.gnd, model='generic') # circuit.model('generic', 'npn') circuit.include(spice_library['2n2222a']) circuit.BJT(1, 'collector', 'base', circuit.gnd, model='2n2222a') print(circuit) # ===== Run simulation ===== simulator = circuit.simulator(temperature=25, nominal_temperature=25)
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
NODELIST.update({i: i for i in range(0, N_NODES)}) VCC_V = '5V' VDD_V = '-5V' HISTORY = {} ELEMLIST = [0, 1, 2, 3, 6] #print(NODELIST_keys) def target(inp): return abs(inp) def evaluator(inp, outp): return (1 / math.sqrt(1 + (abs(target(inp) - outp) * 20))) * ( (abs(inp - (outp)) > abs(inp * 0.05))) if __name__ == "__main__": toolbox = deap.base.Toolbox() toolbox.register("evaluator", evaluator) s = spicega.SpiceGA(toolbox, nodelist=NODELIST,\ elemlist=ELEMLIST,\ ngen=NGEN,\ popsize=POPSIZE,\ n_nodes = 20, mutationpb = MUTPB,\ spice_library=SpiceLibrary(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'libraries'))) s.run()
from pathlib import Path from PySpice.Spice.Library import SpiceLibrary library_path = Path(__file__).resolve().parents[1].joinpath('examples', 'libraries') print(library_path) spice_library = SpiceLibrary(library_path) # print list(spice_library.iter_on_subcircuits()) # print list(spice_library.iter_on_models()) print(list(spice_library.subcircuits)) print(list(spice_library.models))
def _gen_netlist_(self, **kwargs): """ Return a PySpice Circuit generated from a SKiDL circuit. Args: title: String containing the title for the PySpice circuit. libs: String or list of strings containing the paths to directories containing SPICE models. """ if USING_PYTHON2: return None # Create an empty PySpice circuit. title = kwargs.pop("title", "") # Get title and remove it from kwargs. circuit = PySpiceCircuit(title) # Initialize set of libraries to include in the PySpice circuit. includes = set() # Add any models used by the parts. models = set([getattr(part, "model", None) for part in self.parts]) models.discard(None) lib_dirs = set(flatten([kwargs.get("libs", None)])) lib_dirs.discard(None) spice_libs = [SpiceLibrary(dir) for dir in lib_dirs] for model in models: if isinstance(model, (XspiceModel, DeviceModel)): circuit.model(*model.args, **model.kwargs) else: for spice_lib in spice_libs: try: includes.add(spice_lib[model]) except KeyError: pass else: break else: logger.error("Unknown SPICE model: {}".format(model)) # Add any subckt libraries used by the parts. part_names = set( [ getattr(part, "name", None) for part in self.parts if getattr(part, "filename", None) ] ) lib_files = set([getattr(part, "filename", None) for part in self.parts]) lib_files.discard(None) lib_dirs = [os.path.dirname(f) for f in lib_files] spice_libs = [SpiceLibrary(dir) for dir in lib_dirs] for part_name in part_names: for spice_lib in spice_libs: try: includes.add(spice_lib[part_name]) except KeyError: pass else: break else: logger.error("Unknown SPICE subckt: {}".format(part_name)) # Add the models and subckt libraries to the PySpice circuit. for inc in includes: circuit.include(inc) # Add each part in the SKiDL circuit to the PySpice circuit. for part in sorted(self.parts, key=lambda p: str(p.ref)): # Add each part using its add function which will be either # add_part_to_circuit() or add_subcircuit_to_circuit(). try: add_func = part.pyspice["add"] except (AttributeError, KeyError): logger.error("Part has no SPICE model: {}".format(part)) else: add_func(part, circuit) return circuit
#################################################################################################### import PySpice.Logging.Logging as Logging logger = Logging.setup_logging() #################################################################################################### from PySpice.Spice.Netlist import Circuit from PySpice.Spice.Library import SpiceLibrary from PySpice.Unit import * from PySpice.Physics.SemiConductor import ShockleyDiode #################################################################################################### libraries_path = os.path.join(os.environ['PySpice_examples_path'], 'libraries') spice_library = SpiceLibrary(libraries_path) #################################################################################################### #!# For this purpose, we use the common high-speed diode 1N4148. The diode is driven by a variable #!# voltage source through a limiting current resistance. #cm# diode-characteristic-curve-circuit.m4 circuit = Circuit('Diode Characteristic Curve') circuit.include(spice_library['1N4148']) 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)
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()