コード例 #1
0
ファイル: Shared.py プロジェクト: subasah/PySpice
    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')
コード例 #2
0
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
コード例 #3
0
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