Source code for denspp.offline.analog.pyspice_load

import numpy as np
from dataclasses import dataclass
from PySpice.Spice.Netlist import Circuit
from .pyspice_handler import PySpiceHandler


[docs] @dataclass class SettingsPySpice: """Individual data class to configure the electrical device Attributes: type: Type of electrical device ['R': resistor, 'C': capacitor, 'L': inductor, 'RDs': Resistive diode] fs_ana: Sampling frequency of input [Hz] noise_en: Enable noise on output [True / False] dev_value: Value of the selected electrical device temp: Temperature [K] """ type: str fs_ana: float noise_en: bool dev_value: float temp: float
RecommendedSettingsDEV = SettingsPySpice( type='R', fs_ana=50e3, noise_en=False, dev_value=100e3, temp=300 )
[docs] class PySpiceLoad(PySpiceHandler): _settings: SettingsPySpice _type_device: dict _type_string: dict _type_params: dict _params_used: list _circuit: Circuit __sim_time: float def __init__(self, settings_pyspice: SettingsPySpice) -> None: super().__init__(settings_pyspice.temp, True) self._settings = settings_pyspice self._circuit = Circuit("Test") self._type_device = self.__init_dev() self._type_string = self.__init_dev_string() self._type_params = self.__init_params() self.__sim_time = 1.0 self.vcm = 0.0 def __init_dev(self) -> dict: """Initialization of functions to get devices""" dev_type = {'R': self._resistor, 'C': self._capacitor} dev_type.update({'Ds': self._diode_single, 'Dd': self._diode_antiparallel}) dev_type.update({'RDs': self._resistive_diode_single, 'RDd': self._resistive_diode_antiparallel}) dev_type.update({'RaM': self._simple_randles_model}) return dev_type def __init_dev_string(self) -> dict: """Initialization of functions to get devices""" dev_type = {'R': 'Resistor', 'C': 'Capacitor'} dev_type.update({'Ds': 'pn-Diode (single)', 'Dd': 'pn-Diode (anti-parallel)'}) dev_type.update({'RDs': 'Resistive diode (single)', 'RDd': 'Resistive diode (anti-parallel)'}) dev_type.update({'RaM': 'Simple Randles Model'}) return dev_type def __init_params(self) -> dict: """Initialization of Device Parameters""" params_dict = {} params_dict.update({'R': [self._settings.dev_value], 'C': [self._settings.dev_value]}) params_dict.update({'Ds': [1e-12, 1.4, 0.7], 'Dd': [1e-12, 1.4, 0.7]}) params_dict.update({'RDs': [1e-12, 2.8, 0.1, self._settings.dev_value]}) params_dict.update({'RDd': [1e-12, 2.8, 0.1, self._settings.dev_value]}) params_dict.update({'RaM': [10e3, 100e6, 10e-9]}) return params_dict
[docs] def set_simulation_duration(self, sim_time: float) -> None: """Defining the simulation duration for SPICE simulation""" self.__sim_time = sim_time
[docs] def print_types(self) -> None: """Print electrical types in terminal""" print("\n===========================================" "\nAvailable types of electrical devices") for idx, type in enumerate(self._type_device.keys()): print(f"\t#{idx:03d}: {type} = {self._type_string[type]}")
[docs] def get_current(self, u_top: np.ndarray | float, u_bot: np.ndarray | float) -> np.ndarray: """Getting the current response from electrical device Args: u_top: Applied voltage on top electrode [V] u_bot: Applied voltage on bottom electrode [V] Returns: Corresponding current response """ du = u_top - u_bot if self._settings.type in self._type_device.keys(): self.set_src_mode(True) self.load_circuit_model(self._type_device[self._settings.type]()) if isinstance(du, float) or isinstance(du, int): results = self.do_dc_simulation(du, do_print_results=False) i_out0 = results['i_in'] else: self.set_simulation_duration(du.size / self._settings.fs_ana) results = self.do_transient_arbitrary_simulation(du, self.__sim_time, self._settings.fs_ana) i_out0 = results['i_in'] num_dly = i_out0.size-du.size-1 i_out0 = i_out0[num_dly:-1] else: raise "Error: Model not available - Please check!" return np.array(i_out0)
[docs] def get_voltage(self, i_in: np.ndarray, u_inn: np.ndarray | float) -> np.ndarray: """Getting the voltage response from electrical device Args: i_in: Applied current input [A] u_inn: Negative input | bottom electrode | reference voltage [V] Returns: Corresponding voltage response """ if isinstance(i_in, float) or isinstance(u_inn, int): vout = np.zeros((1,), dtype=float) else: vout = np.zeros(i_in.shape, dtype=float) if self._settings.type in self._type_device.keys(): self.set_src_mode(False) self.set_simulation_duration(i_in.size / self._settings.fs_ana) self.load_circuit_model(self._type_device[self._settings.type]()) self.do_transient_arbitrary_simulation(i_in, self.__sim_time, self._settings.fs_ana) results = self.__get_results(3) vout = results['v_in'] + u_inn num_dly = vout.size - i_in.size - 1 vout = vout[num_dly:] else: print("Error: Model not available - Please check!") return vout
[docs] def plot_fit_curve(self, start_value: float=-5.0, stop_value: float=+5.0, step_size: float=0.1, do_logy: bool=False, path2save: str='', show_plot: bool=False) -> None: """Plotting the output of the polynom fit function Args: start_value: Starting point of DC Sweep stop_value: End point of DC Sweep step_size: Step size of DC Sweep do_logy: Do logarithmic plotting on y-scale path2save: Path for saving the plot show_plot: Showing and blocking the plot Returns: None """ self.set_src_mode(True) self.load_circuit_model(self._type_device[self._settings.type]()) self.do_dc_sweep_simulation(start_value, stop_value, step_size) self.plot_iv_curve(do_logy, path2save, show_plot)
# --------------- OVERVIEW OF MODELS ------------------------- def _resistor(self) -> Circuit: """Using resistor as load element""" params = self._type_params[self._settings.type] circuit = Circuit("Resistive Load") circuit.R(1, 'input', 'output', params[0]) circuit.V('cm', 'output', circuit.gnd, self.vcm) return circuit def _capacitor(self) -> Circuit: """Using capacitor as load element""" params = self._type_params[self._settings.type] circuit = Circuit("Capacitive Load") circuit.R(0, 'input', 'middle', 100) circuit.C(0, 'middle', 'output', params[0]) circuit.V('cm', 'output', circuit.gnd, self.vcm) return circuit def _diode_single(self) -> Circuit: """Using 1N4148 diode as load element""" params = self._type_params[self._settings.type] circuit0 = Circuit("Diode_1N4148") circuit0.model('1N4148', 'D', IS=4.352e-9, N=1.906, BV=110, IBV=0.0001, RS=0.6458, CJO=7.048e-13, V=0.869, M=0.03, FC=0.5, TT=3.48E-9) circuit0.R(0, 'input', 'middle', 10) circuit0.Diode(0, 'middle', 'output', model='1N4148') circuit0.V('cm', 'output', circuit0.gnd, self.vcm) return circuit0 def _diode_antiparallel(self) -> Circuit: """Using 1N4148 in anti-parallel configuration as load element""" params = self._type_params[self._settings.type] circuit0 = Circuit("Diode_1N4148 (Antiparallel)") circuit0.model('1N4148', 'D', IS=4.352e-9, N=1.906, BV=110, IBV=0.0001, RS=0.6458, CJO=7.048e-13, V=0.869, M=0.03, FC=0.5, TT=3.48E-9) circuit0.Diode(0, 'input', 'middle', model='1N4148') circuit0.Diode(1, 'middle', 'input', model='1N4148') circuit0.R(0, 'middle', 'output', 10) circuit0.V('cm', 'output', circuit0.gnd, self.vcm) return circuit0 def _resistive_diode_single(self) -> Circuit: """Using resistive diode as load element""" params = self._type_params[self._settings.type] circuit0 = Circuit("Resistive Diode") circuit0.model('1N4148', 'D', IS=4.352e-9, N=1.906, BV=110, IBV=0.0001, RS=0.6458, CJO=7.048e-13, V=0.869, M=0.03, FC=0.5, TT=3.48E-9) circuit0.R(0, 'input', 'middle', params[3]) circuit0.Diode(0, 'middle', 'output', model='1N4148') circuit0.V('cm', 'output', circuit0.gnd, self.vcm) return circuit0 def _resistive_diode_antiparallel(self) -> Circuit: """Using resistive diode in anti-parallel configuration as load element""" params = self._type_params[self._settings.type] circuit0 = Circuit("Resistive Diode (Antiparallel)") circuit0.model('1N4148', 'D', IS=4.352e-9, N=1.906, BV=110, IBV=0.0001, RS=0.6458, CJO=7.048e-13, V=0.869, M=0.03, FC=0.5, TT=3.48E-9) circuit0.R(0, 'input', 'middle', params[3]) circuit0.Diode(0, 'middle', 'output', model='1N4148') circuit0.Diode(1, 'output', 'middle', model='1N4148') circuit0.V('cm', 'output', circuit0.gnd, self.vcm) return circuit0 def _simple_randles_model(self) -> Circuit: """Using simple Randles model as load element""" params = self._type_params[self._settings.type] circuit0 = Circuit("Simple Randles Model") circuit0.R('tis', 'input', 'middle', params[0]) circuit0.R('far', 'middle', 'output', params[1]) circuit0.C('dl', 'middle', 'output', params[2]) circuit0.V('cm', 'output', circuit0.gnd, 0.0) return circuit0