def _init_ngspice(self, send_data): # Ngspice API: ngSpice_Init ngSpice_Init_Sync self._send_char_c = ffi.callback('int (char *, int, void *)', self._send_char) self._send_stat_c = ffi.callback('int (char *, int, void *)', self._send_stat) self._exit_c = ffi.callback('int (int, bool, bool, int, void *)', self._exit) self._send_init_data_c = ffi.callback('int (pvecinfoall, int, void *)', self._send_init_data) self._background_thread_running_c = ffi.callback('int (bool, int, void *)', self._background_thread_running) if send_data: self._send_data_c = ffi.callback('int (pvecvaluesall, int, int, void *)', self._send_data) else: self._send_data_c = FFI.NULL self._get_vsrc_data_c = ffi.callback('int (double *, double, char *, int, void *)', self._get_vsrc_data) self._get_isrc_data_c = ffi.callback('int (double *, double, char *, int, void *)', self._get_isrc_data) self_c = ffi.new_handle(self) self._self_c = self_c # To prevent garbage collection rc = self._ngspice_shared.ngSpice_Init(self._send_char_c, self._send_stat_c, self._exit_c, self._send_data_c, self._send_init_data_c, self._background_thread_running_c, self_c) if rc: raise NameError("Ngspice_Init returned {}".format(rc)) ngspice_id_c = ffi.new('int *', self._ngspice_id) self._ngspice_id = ngspice_id_c # To prevent garbage collection rc = self._ngspice_shared.ngSpice_Init_Sync(self._get_vsrc_data_c, self._get_isrc_data_c, FFI.NULL, # GetSyncData ngspice_id_c, self_c) if rc: raise NameError("Ngspice_Init_Sync returned {}".format(rc)) self._get_version() try: self._simulation_type = EnumFactory('SimulationType', SIMULATION_TYPE[self._ngspice_version]) except KeyError: self._simulation_type = EnumFactory('SimulationType', SIMULATION_TYPE['last']) self._logger.warning("Unsupported Ngspice version {}".format(self._ngspice_version)) self._type_to_unit = { self._simulation_type.time: u_s, self._simulation_type.voltage: u_V, self._simulation_type.current: u_A, self._simulation_type.frequency: u_Hz, self._simulation_type.capacitance: u_F, } # Prevent paging output of commands (hangs) self.set('nomoremode')
class NgSpiceShared: _logger = _module_logger.getChild('NgSpiceShared') simulation_type = EnumFactory( 'SimulationType', ('no_type', 'time', 'frequency', 'voltage', 'current', 'output_noise_density', 'output_noise', 'input_noise_density', 'input_noise', 'pole', 'zero', 's_parameter', 'temperature', 'res', 'impedance', 'admittance', 'power', 'phase', 'db', 'capacitance', 'charge')) ############################################## def __init__(self, ngspice_id=0, send_data=False): """ Set the *send_data* flag if you want to enable the output callback. Set the *ngspice_id* to an integer value if you want to run NgSpice in parallel. """ self._ngspice_id = ngspice_id self._load_library() self._init_ngspice(send_data) ############################################## def _load_library(self): api_path = os.path.join(os.path.dirname(__file__), 'api.h') with open(api_path) as f: ffi.cdef(f.read()) if not self._ngspice_id: library_prefix = '' else: library_prefix = '{}'.format(self._ngspice_id) library_file = 'libngspice{}.so'.format(library_prefix) self._ngspice_shared = ffi.dlopen(library_file) ############################################## def _init_ngspice(self, send_data): self._send_char_c = ffi.callback('int (char *, int, void *)', self._send_char) self._send_stat_c = ffi.callback('int (char *, int, void *)', self._send_stat) self._exit_c = ffi.callback('int (int, bool, bool, int, void *)', self._exit) self._send_init_data_c = ffi.callback('int (pvecinfoall, int, void *)', self._send_init_data) if send_data: self._send_data_c = ffi.callback( 'int (pvecvaluesall, int, int, void *)', self._send_data) else: self._send_data_c = ffi.NULL self._get_vsrc_data_c = ffi.callback( 'int (double *, double, char *, int, void *)', self._get_vsrc_data) self._get_isrc_data_c = ffi.callback( 'int (double *, double, char *, int, void *)', self._get_isrc_data) self_c = ffi.new_handle(self) self._self_c = self_c # To prevent garbage collection rc = self._ngspice_shared.ngSpice_Init( self._send_char_c, self._send_stat_c, self._exit_c, self._send_data_c, self._send_init_data_c, ffi.NULL, # BGThreadRunning self_c) if rc: raise NameError("Ngspice_Init returned {}".format(rc)) ngspice_id_c = ffi.new('int *', self._ngspice_id) self._ngspice_id = ngspice_id_c # To prevent garbage collection rc = self._ngspice_shared.ngSpice_Init_Sync( self._get_vsrc_data_c, self._get_isrc_data_c, ffi.NULL, # GetSyncData ngspice_id_c, self_c) if rc: raise NameError("Ngspice_Init_Sync returned {}".format(rc)) ############################################## @staticmethod def _send_char(message, ngspice_id, user_data): """FFI Callback""" self = ffi.from_handle(user_data) return self.send_char(ffi_string_utf8(message), ngspice_id) ############################################## @staticmethod def _send_stat(message, ngspice_id, user_data): """FFI Callback""" self = ffi.from_handle(user_data) return self.send_stat(ffi_string_utf8(message), ngspice_id) ############################################## @staticmethod def _exit(exit_status, immediate_unloding, quit_exit, ngspice_id, user_data): """FFI Callback""" self = ffi.from_handle(user_data) self._logger.debug('ngspice_id-{} exit {} {} {} {}'.format( ngspice_id, exit_status, immediate_unloding, quit_exit)) return exit_status ############################################## @staticmethod def _send_data(data, number_of_vectors, ngspice_id, user_data): """FFI Callback""" self = ffi.from_handle(user_data) self._logger.debug('ngspice_id-{} send_data [{}]'.format( ngspice_id, data.vecindex)) actual_vector_values = {} for i in range(int(number_of_vectors)): actual_vector_value = data.vecsa[i] vector_name = ffi_string_utf8(actual_vector_value.name) value = complex(actual_vector_value.creal, actual_vector_value.cimag) actual_vector_values[vector_name] = value self._logger.debug(' Vector: {} {}'.format(vector_name, value)) return self.send_data(actual_vector_values, number_of_vectors, ngspice_id) ############################################## @staticmethod def _send_init_data(data, ngspice_id, user_data): """FFI Callback""" self = ffi.from_handle(user_data) if self._logger.isEnabledFor(logging.DEBUG): self._logger.debug( 'ngspice_id-{} send_init_data'.format(ngspice_id)) number_of_vectors = data.veccount for i in range(number_of_vectors): self._logger.debug(' Vector: ' + ffi_string_utf8(data.vecs[i].vecname)) return self.send_init_data( data, ngspice_id) # Fixme: should be a Python object ############################################## @staticmethod def _get_vsrc_data(voltage, time, node, ngspice_id, user_data): """FFI Callback""" self = ffi.from_handle(user_data) return self.get_vsrc_data(voltage, time, ffi_string_utf8(node), ngspice_id) ############################################## @staticmethod def _get_isrc_data(current, time, node, ngspice_id, user_data): """FFI Callback""" self = ffi.from_handle(user_data) return self.get_isrc_data(current, time, ffi_string_utf8(node), ngspice_id) ############################################## def send_char(self, message, ngspice_id): """ Reimplement this callback in a subclass to process logging messages from the simulator. """ self._logger.debug('ngspice-{} send_char {}'.format( ngspice_id, message)) return 0 ############################################## def send_stat(self, message, ngspice_id): """ Reimplement this callback in a subclass to process statistic messages from the simulator. """ self._logger.debug('ngspice-{} send_stat {}'.format( ngspice_id, message)) return 0 ############################################## def send_data(self, actual_vector_values, number_of_vectors, ngspice_id): """ Reimplement this callback in a subclass to process the vector actual values. """ return 0 ############################################## def send_init_data(self, data, ngspice_id): """ Reimplement this callback in a subclass to process the initial data. """ return 0 ############################################## def get_vsrc_data(self, voltage, time, node, ngspice_id): """ Reimplement this callback in a subclass to provide external voltage source. """ self._logger.debug('ngspice_id-{} get_vsrc_data @{} node {}'.format( ngspice_id, time, node)) return 0 ############################################## def get_isrc_data(self, current, time, node, ngspice_id): """ Reimplement this callback in a subclass to provide external current source. """ self._logger.debug('ngspice_id-{} get_isrc_data @{} node {}'.format( ngspice_id, time, node)) return 0 ############################################## def load_circuit(self, circuit): """ Load the given circuit string. """ circuit_lines = [line for line in str(circuit).split('\n') if line] circuit_lines_keepalive = [ ffi.new("char[]", line.encode('utf8')) for line in circuit_lines ] circuit_lines_keepalive += [ffi.NULL] circuit_array = ffi.new("char *[]", circuit_lines_keepalive) rc = self._ngspice_shared.ngSpice_Circ(circuit_array) if rc: raise NameError("ngSpice_Circ returned {}".format(rc)) # for line in circuit_lines: # rc = self._ngspice_shared.ngSpice_Command('circbyline ' + line) # if rc: # raise NameError("ngSpice_Command circbyline returned {}".format(rc)) ############################################## def run(self): """ Run the simulation in the background thread and wait until the simulation is done. """ rc = self._ngspice_shared.ngSpice_Command(b'bg_run') if rc: raise NameError("ngSpice_Command bg_run returned {}".format(rc)) time.sleep(.1) # required before to test if the simulation is running while (self._ngspice_shared.ngSpice_running()): time.sleep(.1) self._logger.debug("Simulation is done") ############################################## def _convert_string_array(self, array): strings = [] i = 0 while (True): if array[i] == ffi.NULL: break else: strings.append(ffi_string_utf8(array[i])) i += 1 return strings ############################################## @property def plot_names(self): """ Return the list of plot names. """ return self._convert_string_array( self._ngspice_shared.ngSpice_AllPlots()) ############################################## def plot(self, plot_name): """ Return the corresponding plot. """ plot = Plot(plot_name) all_vectors_c = self._ngspice_shared.ngSpice_AllVecs( plot_name.encode('utf8')) i = 0 while (True): if all_vectors_c[i] == ffi.NULL: break else: vector_name = ffi_string_utf8(all_vectors_c[i]) name = '.'.join((plot_name, vector_name)) vector_info = self._ngspice_shared.ngGet_Vec_Info( name.encode('utf8')) vector_type = vector_info.v_type length = vector_info.v_length self._logger.debug( "vector[{}] {} type {} flags {} length {}".format( i, vector_name, vector_type, vector_info.v_flags, length)) # flags: VF_REAL = 1 << 0, VF_COMPLEX = 1 << 1 if vector_info.v_compdata == ffi.NULL: # for k in xrange(length): # print(" [{}] {}".format(k, vector_info.v_realdata[k])) array = np.frombuffer(ffi.buffer(vector_info.v_realdata, length * 8), dtype=np.float64) else: # for k in xrange(length): # value = vector_info.v_compdata[k] # print(ffi.addressof(value, field='cx_real'), ffi.addressof(value, field='cx_imag')) # print(" [{}] {} + i {}".format(k, value.cx_real, value.cx_imag)) tmp_array = np.frombuffer(ffi.buffer( vector_info.v_compdata, length * 8 * 2), dtype=np.float64) array = np.array(tmp_array[0::2], dtype=np.complex64) array.imag = tmp_array[1::2] plot[vector_name] = Vector(vector_name, self.simulation_type[vector_type], array) i += 1 return plot
class NgSpiceShared: _logger = _module_logger.getChild('NgSpiceShared') SIMULATION_TYPE = EnumFactory( 'SimulationType', ('no_type', 'time', 'frequency', 'voltage', 'current', 'output_noise_density', 'output_noise', 'input_noise_density', 'input_noise', 'pole', 'zero', 's_parameter', 'temperature', 'res', 'impedance', 'admittance', 'power', 'phase', 'db', 'capacitance', 'charge')) NGSPICE_PATH = None LIBRARY_PATH = None __MAX_COMMAND_LENGTH__ = 1023 ############################################## _instances = {} @classmethod def new_instance(cls, ngspice_id=0, send_data=False): # Fixme: send_data if ngspice_id in cls._instances: return cls._instances[ngspice_id] else: cls._logger.info("New instance for id {}".format(ngspice_id)) instance = cls(ngspice_id=ngspice_id, send_data=send_data) cls._instances[ngspice_id] = instance return instance ############################################## def __init__(self, ngspice_id=0, send_data=False): """ Set the *send_data* flag if you want to enable the output callback. Set the *ngspice_id* to an integer value if you want to run NgSpice in parallel. """ self._ngspice_id = ngspice_id self._stdout = [] self._stderr = [] self._load_library() self._init_ngspice(send_data) self._is_running = False ############################################## def _load_library(self): if ConfigInstall.OS.on_windows: # https://sourceforge.net/p/ngspice/discussion/133842/thread/1cece652/#4e32/5ab8/9027 # When environment variable SPICE_LIB_DIR is empty, ngspice looks in C:\Spice64\share\ngspice\scripts # Else it tries %SPICE_LIB_DIR%\scripts\spinit if 'SPICE_LIB_DIR' not in os.environ: os.environ['SPICE_LIB_DIR'] = os.path.join( self.NGSPICE_PATH, 'share', 'ngspice') api_path = os.path.join(os.path.dirname(__file__), 'api.h') with open(api_path) as f: ffi.cdef(f.read()) if not self._ngspice_id: library_prefix = '' else: library_prefix = '{}'.format(self._ngspice_id) library_path = self.LIBRARY_PATH.format(library_prefix) self._ngspice_shared = ffi.dlopen(library_path) ############################################## def _init_ngspice(self, send_data): self._send_char_c = ffi.callback('int (char *, int, void *)', self._send_char) self._send_stat_c = ffi.callback('int (char *, int, void *)', self._send_stat) self._exit_c = ffi.callback('int (int, bool, bool, int, void *)', self._exit) self._send_init_data_c = ffi.callback('int (pvecinfoall, int, void *)', self._send_init_data) self._background_thread_running_c = ffi.callback( 'int (bool, int, void *)', self._background_thread_running) if send_data: self._send_data_c = ffi.callback( 'int (pvecvaluesall, int, int, void *)', self._send_data) else: self._send_data_c = ffi.NULL self._get_vsrc_data_c = ffi.callback( 'int (double *, double, char *, int, void *)', self._get_vsrc_data) self._get_isrc_data_c = ffi.callback( 'int (double *, double, char *, int, void *)', self._get_isrc_data) self_c = ffi.new_handle(self) self._self_c = self_c # To prevent garbage collection rc = self._ngspice_shared.ngSpice_Init( self._send_char_c, self._send_stat_c, self._exit_c, self._send_data_c, self._send_init_data_c, self._background_thread_running_c, self_c) if rc: raise NameError("Ngspice_Init returned {}".format(rc)) ngspice_id_c = ffi.new('int *', self._ngspice_id) self._ngspice_id = ngspice_id_c # To prevent garbage collection rc = self._ngspice_shared.ngSpice_Init_Sync( self._get_vsrc_data_c, self._get_isrc_data_c, ffi.NULL, # GetSyncData ngspice_id_c, self_c) if rc: raise NameError("Ngspice_Init_Sync returned {}".format(rc)) # Prevent paging output of commands (hangs) self.set('nomoremode') ############################################## @staticmethod def _send_char(message_c, ngspice_id, user_data): """Callback for sending output from stdout, stderr to caller""" self = ffi.from_handle(user_data) message = ffi_string_utf8(message_c) prefix, _, content = message.partition(' ') if prefix == 'stderr': self._stderr.append(content) self._logger.error(content) else: self._stdout.append(content) # Fixme: ??? return self.send_char(message, ngspice_id) ############################################## @staticmethod def _send_stat(message, ngspice_id, user_data): """Callback for simulation status to caller""" self = ffi.from_handle(user_data) return self.send_stat(ffi_string_utf8(message), ngspice_id) ############################################## @staticmethod def _exit(exit_status, immediate_unloding, quit_exit, ngspice_id, user_data): """Callback for asking for a reaction after controlled exit""" self = ffi.from_handle(user_data) self._logger.debug( 'ngspice_id-{} exit status={} immediate_unloding={} quit_exit={}'. format(ngspice_id, exit_status, immediate_unloding, quit_exit)) return exit_status ############################################## @staticmethod def _send_data(data, number_of_vectors, ngspice_id, user_data): """Callback to send back actual vector data""" self = ffi.from_handle(user_data) # self._logger.debug('ngspice_id-{} send_data [{}]'.format(ngspice_id, data.vecindex)) actual_vector_values = {} for i in range(int(number_of_vectors)): actual_vector_value = data.vecsa[i] vector_name = ffi_string_utf8(actual_vector_value.name) value = complex(actual_vector_value.creal, actual_vector_value.cimag) actual_vector_values[vector_name] = value # self._logger.debug(' Vector: {} {}'.format(vector_name, value)) return self.send_data(actual_vector_values, number_of_vectors, ngspice_id) ############################################## @staticmethod def _send_init_data(data, ngspice_id, user_data): """Callback to send back initialization vector data""" self = ffi.from_handle(user_data) # if self._logger.isEnabledFor(logging.DEBUG): # self._logger.debug('ngspice_id-{} send_init_data'.format(ngspice_id)) # number_of_vectors = data.veccount # for i in range(number_of_vectors): # self._logger.debug(' Vector: ' + ffi_string_utf8(data.vecs[i].vecname)) return self.send_init_data( data, ngspice_id) # Fixme: should be a Python object ############################################## @staticmethod def _background_thread_running(is_running, ngspice_id, user_data): """Callback to indicate if background thread is runnin""" self = ffi.from_handle(user_data) self._logger.debug('ngspice_id-{} background_thread_running {}'.format( ngspice_id, is_running)) self._is_running = is_running ############################################## @staticmethod def _get_vsrc_data(voltage, time, node, ngspice_id, user_data): """FFI Callback""" self = ffi.from_handle(user_data) return self.get_vsrc_data(voltage, time, ffi_string_utf8(node), ngspice_id) ############################################## @staticmethod def _get_isrc_data(current, time, node, ngspice_id, user_data): """FFI Callback""" self = ffi.from_handle(user_data) return self.get_isrc_data(current, time, ffi_string_utf8(node), ngspice_id) ############################################## def send_char(self, message, ngspice_id): """ Reimplement this callback in a subclass to process logging messages from the simulator. """ # self._logger.debug('ngspice-{} send_char {}'.format(ngspice_id, message)) return 0 ############################################## def send_stat(self, message, ngspice_id): """ Reimplement this callback in a subclass to process statistic messages from the simulator. """ # self._logger.debug('ngspice-{} send_stat {}'.format(ngspice_id, message)) return 0 ############################################## def send_data(self, actual_vector_values, number_of_vectors, ngspice_id): """ Reimplement this callback in a subclass to process the vector actual values. """ return 0 ############################################## def send_init_data(self, data, ngspice_id): """ Reimplement this callback in a subclass to process the initial data. """ return 0 ############################################## def get_vsrc_data(self, voltage, time, node, ngspice_id): """ Reimplement this callback in a subclass to provide external voltage source. """ self._logger.debug('ngspice_id-{} get_vsrc_data @{} node {}'.format( ngspice_id, time, node)) return 0 ############################################## def get_isrc_data(self, current, time, node, ngspice_id): """ Reimplement this callback in a subclass to provide external current source. """ self._logger.debug('ngspice_id-{} get_isrc_data @{} node {}'.format( ngspice_id, time, node)) return 0 ############################################## @staticmethod def _convert_string_array(array): strings = [] i = 0 while (True): if array[i] == ffi.NULL: break else: strings.append(ffi_string_utf8(array[i])) i += 1 return strings ############################################## @staticmethod def _to_python(value): try: return int(value) except ValueError: try: # Fixme: return float(value.replace(',', '.')) return float(value) except ValueError: return str(value) ############################################## @staticmethod def _lines_to_dicts(lines): values = dict(description=lines[0]) values.update({ parts[0]: NgSpiceShared._to_python(parts[1]) for parts in map(str.split, lines) }) return values ############################################## @property def is_running(self): return self._is_running ############################################## def clear_output(self): self._stdout = [] self._stderr = [] ############################################## @property def stdout(self): return os.linesep.join(self._stdout) @property def stderr(self): return os.linesep.join(self._stderr) ############################################## def exec_command(self, command, join_lines=True): """ Execute a command and return the output. """ if len(command) > self.__MAX_COMMAND_LENGTH__: raise ValueError('Command must not exceed {} characters'.format( self.__MAX_COMMAND_LENGTH__)) self._logger.debug('Execute command: {}'.format(command)) self.clear_output() rc = self._ngspice_shared.ngSpice_Command(command.encode('ascii')) if rc: raise NameError("ngSpice_Command '{}' returned {}".format( command, rc)) if join_lines: return self.stdout else: return self._stdout ############################################## def has_xspice(self): """Return True if libngspice was compiled with XSpice support """ return '** XSPICE extensions included' in cmd('version -f') ############################################## def has_cider(self): """Return True if libngspice was compiled with CIDER support """ return '** CIDER 1.b1 (CODECS simulator) included' in cmd('version -f') ############################################## def _alter(self, command, item, kwargs): device_name = device.lower() for key, value in kwargs.items(): if isinstance(value, (list, tuple)): value = '[ ' + ' '.join(value) + ' ]' self.exec_command('{} {} {} = {}'.format(command, device_name, key, value)) ############################################## def alter_device(self, device, **kwargs): """Alter device parameters""" self._alter('alter', device, kwargs) ############################################## def alter_model(self, model, **kwargs): """Alter model parameters""" self._alter('altermod', device, kwargs) ############################################## def delete(self, debug_number): """Remove a trace or breakpoint""" self.exec_command('delete {}'.format(debug_number)) ############################################## def destroy(self, plot_name='all'): """Release the memory holding the output data (the given plot or all plots) for the specified runs.""" self.exec_command('destroy ' + plot_name) ############################################## def device_help(self, device): """Shows the user information about the devices available in the simulator. """ return self.exec_command('devhelp ' + device.lower()) ############################################## def save(self, vector): self.exec_command('save ' + vector) ############################################## def show(self, device): command = 'show ' + device.lower() lines = self.exec_command(command, join_lines=False) values = self._lines_to_dicts(lines) return values ############################################## def showmod(self, device): command = 'showmod ' + device.lower() lines = self.exec_command(command, join_lines=False) values = self._lines_to_dicts(lines) return values ############################################## def source(file_path): """Read a ngspice input file""" self.exec_command('source ' + file_path) ############################################## def option(self, **kwargs): """Set any of the simulator variables.""" for key, value in kwargs.items(): self.exec_command('option {} = {}'.format(command, key, value)) ############################################## def quit(self): self.set('noaskquit') return self.exec_command('quit') ############################################## def remove_circuit(self): """Removes the current circuit from the list of circuits sourced into ngspice.""" self.exec_command('remcirc') ############################################## def reset(self): """Throw out any intermediate data in the circuit (e.g, after a breakpoint or after one or more analyses have been done already), and re-parse the input file. The circuit can then be re-run from it’s initial state, overriding the affect of any set or alter commands. """ self.exec_command('reset') ############################################## def ressource_usage(self, *ressources): """Print resource usage statistics. If any resources are given, just print the usage of that resource. Most resources require that a circuit be loaded. Currently valid resources are: * decklineno Number of lines in deck * netloadtime Nelist loading time * netparsetime Netlist parsing time * elapsed The amount of time elapsed since the last rusage elapsed call. * faults Number of page faults and context switches (BSD only). * space Data space used. * time CPU time used so far. * temp Operating temperature. * tnom Temperature at which device parameters were measured. * equations Circuit Equations * time Total Analysis Time * totiter Total iterations * accept Accepted time-points * rejected Rejected time-points * loadtime Time spent loading the circuit matrix and RHS. * reordertime Matrix reordering time * lutime L-U decomposition time * solvetime Matrix solve time * trantime Transient analysis time * tranpoints Transient time-points * traniter Transient iterations * trancuriters Transient iterations for the last time point* * tranlutime Transient L-U decomposition time * transolvetime Transient matrix solve time * everything All of the above. """ if not ressources: ressources = ['everything'] command = 'rusage ' + ' '.join(ressources) lines = self.exec_command(command, join_lines=False) values = {} for line in lines: if '=' in line: parts = line.split(' = ') else: parts = line.split(': ') values[parts[0]] = NgSpiceShared._to_python(parts[1]) return values ############################################## def set(self, *args, **kwargs): """Set the value of variables""" for key in args: self.exec_command('set {}'.format(key)) for key, value in kwargs.items(): self.exec_command('option {} = {}'.format(key, value)) ############################################## def set_circuit(self, name): """Change the current circuit""" self.exec_command('setcirc {}'.format(name)) ############################################## def status(self): """Display breakpoint information""" return self.exec_command('status') ############################################## def step(self, number_of_steps=None): """Run a fixed number of time-points""" if step is not None: self.exec_command('step {}'.format(number_of_steps)) else: self.exec_command('step') ############################################## def stop(self, *args, **kwargs): """Set a breakpoint. Examples:: ngspice.stop('v(out) > 1', 'v(1) > 10', after=10) A when condition can use theses symbols: = <> > < >= <=. """ command = 'stop' if 'after' in kwargs: command += ' after {}'.format(kwargs['after']) for condition in args: command += ' when {}'.format(condition) self.exec_command(command) ############################################## def trace(self, *args): """Trace nodes""" self.exec_command('trace ' + ' '.join(args)) ############################################## def unset(self, *args): """Unset variables""" for key in args: self.exec_command('unset {}'.format(key)) ############################################## def where(self): """Identify troublesome node or device""" return self.exec_command('where') ############################################## def load_circuit(self, circuit): """Load the given circuit string.""" circuit_lines = [ line for line in str(circuit).split(os.linesep) if line ] circuit_lines_keepalive = [ ffi.new("char[]", line.encode('utf8')) for line in circuit_lines ] circuit_lines_keepalive += [ffi.NULL] circuit_array = ffi.new("char *[]", circuit_lines_keepalive) rc = self._ngspice_shared.ngSpice_Circ(circuit_array) if rc: raise NameError("ngSpice_Circ returned {}".format(rc)) # for line in circuit_lines: # rc = self._ngspice_shared.ngSpice_Command('circbyline ' + line) # if rc: # raise NameError("ngSpice_Command circbyline returned {}".format(rc)) ############################################## def run(self, background=False): """ Run the simulation. """ # in the background thread and wait until the simulation is done command = b'bg_run' if background else b'run' rc = self._ngspice_shared.ngSpice_Command(command) if rc: raise NameError("ngSpice_Command run returned {}".format(rc)) if background: self._is_running = True else: self._logger.debug("Simulation is done") # time.sleep(.1) # required before to test if the simulation is running # while (self._ngspice_shared.ngSpice_running()): # time.sleep(.1) # self._logger.debug("Simulation is done") ############################################## def halt(self): """ Halt the simulation in the background thread. """ rc = self._ngspice_shared.ngSpice_Command(b'bg_halt') if rc: raise NameError("ngSpice_Command bg_halt returned {}".format(rc)) ############################################## def resume(self, background=True): """ Halt the simulation in the background thread. """ command = b'bg_resume' if background else b'resume' rc = self._ngspice_shared.ngSpice_Command(command) if rc: raise NameError("ngSpice_Command bg_resume returned {}".format(rc)) ############################################## @property def plot_names(self): """ Return the list of plot names. """ return self._convert_string_array( self._ngspice_shared.ngSpice_AllPlots()) ############################################## @property def last_plot(self): """ Return the last plot name. """ return self.plot_names[0] ############################################## @staticmethod def _flags_to_str(flags): # enum dvec_flags { # VF_REAL = (1 << 0), // The data is real. # VF_COMPLEX = (1 << 1), // The data is complex. # VF_ACCUM = (1 << 2), // writedata should save this vector. # VF_PLOT = (1 << 3), // writedata should incrementally plot it. # VF_PRINT = (1 << 4), // writedata should print this vector. # VF_MINGIVEN = (1 << 5), // The v_minsignal value is valid. # VF_MAXGIVEN = (1 << 6), // The v_maxsignal value is valid. # VF_PERMANENT = (1 << 7) // Don't garbage collect this vector. # }; if flags & 1: return 'real' elif flags & 2: return 'complex' ############################################## @staticmethod def _vector_is_real(flags): return flags & 1 ############################################## @staticmethod def _vector_is_complex(flags): return flags & 2 ############################################## def plot(self, simulation, plot_name): """ Return the corresponding plot. """ # plot_name is for example dc with an integer suffix which is increment for each run plot = Plot(simulation, plot_name) all_vectors_c = self._ngspice_shared.ngSpice_AllVecs( plot_name.encode('utf8')) i = 0 while (True): if all_vectors_c[i] == ffi.NULL: break else: vector_name = ffi_string_utf8(all_vectors_c[i]) name = '.'.join((plot_name, vector_name)) vector_info = self._ngspice_shared.ngGet_Vec_Info( name.encode('utf8')) vector_type = self.SIMULATION_TYPE[vector_info.v_type] length = vector_info.v_length # self._logger.debug("vector[{}] {} type {} flags {} length {}".format(i, # vector_name, # vector_type, # self._flags_to_str(vector_info.v_flags), # length)) if vector_info.v_compdata == ffi.NULL: # for k in xrange(length): # print(" [{}] {}".format(k, vector_info.v_realdata[k])) tmp_array = np.frombuffer(ffi.buffer( vector_info.v_realdata, length * 8), dtype=np.float64) array = np.array(tmp_array, dtype=tmp_array.dtype) # copy data else: # for k in xrange(length): # value = vector_info.v_compdata[k] # print(ffi.addressof(value, field='cx_real'), ffi.addressof(value, field='cx_imag')) # print(" [{}] {} + i {}".format(k, value.cx_real, value.cx_imag)) tmp_array = np.frombuffer(ffi.buffer( vector_info.v_compdata, length * 8 * 2), dtype=np.float64) array = np.array(tmp_array[0::2], dtype=np.complex64) array.imag = tmp_array[1::2] plot[vector_name] = Vector(vector_name, vector_type, array) i += 1 return plot