>
where TD or F, NL must be specified.
Keyword Parameters:
:attr:`impedance`
alias:`Z0`
:attr:`time_delay`
alias:`TD`
:attr:`frequency`
alias:`F`
:attr:`normalized_length`
alias:`NL`
Attributes:
:attr:`impedance`
:attr:`time_delay`
:attr:`frequency`
:attr:`normalized_length`
Note: Either time_delay or frequency must be given.
"""
alias = 'TransmissionLine'
prefix = 'T'
impedance = FloatKeyParameter('Z0', default=50)
time_delay = FloatKeyParameter('TD')
frequency = FloatKeyParameter('F')
normalized_length = FloatKeyParameter('NL')
##############################################
def __init__(self, name,
input_node_plus, input_node_minus,
output_node_plus, output_node_minus,
*args, **kwargs):
# check: ^ xor, & bitwise and
if not (('time_delay' in kwargs) ^
(('frequency' in kwargs) & ('normalized_length' in kwargs))):
raise NameError('Either TD or F, NL must be specified')
super().__init__(name,
output_node_plus, output_node_minus,
input_node_plus, input_node_minus, # Fixme: inverted inputs
*args, **kwargs)
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/ElementParameter.py 0000664 0000000 0000000 00000024566 13053355342 0021521 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This modules implements the machinery to define element's parameters as descriptor."""
####################################################################################################
from ..Unit.Units import Unit
####################################################################################################
class ParameterDescriptor:
"""This base class implements a descriptor for element parameters.
Public Attributes:
:attr:`attribute_name`
Name of the attribute in the element's class
:attr:`default_value`
The default value
"""
##############################################
def __init__(self, default=None):
self._default_value = default
self._attribute_name = None
##############################################
@property
def default_value(self):
return self._default_value
@property
def attribute_name(self):
return self._attribute_name
@attribute_name.setter
def attribute_name(self, name):
self._attribute_name = name
##############################################
def __get__(self, instance, owner=None):
try:
return getattr(instance, '_' + self._attribute_name)
except AttributeError:
return self.default_value
##############################################
def __set__(self, instance, value):
setattr(instance, '_' + self._attribute_name, value)
##############################################
def __repr__(self):
return self.__class__.__name__
##############################################
def validate(self, value):
"""Validate the parameter's value."""
return value
##############################################
def nonzero(self, instance):
return self.__get__(instance) is not None
##############################################
def to_str(self, instance):
"""Convert the parameter's value to SPICE syntax."""
raise NotImplementedError
##############################################
def __lt__(self, other):
return self._attribute_name < other.attribute_name
####################################################################################################
class PositionalElementParameter(ParameterDescriptor):
"""This class implements a descriptor for positional element parameters.
Public Attributes:
:attr:`key_parameter`
Flag to specify if the parameter is passed as key parameter in Python
:attr:`position`
Position of the parameter in the element definition
"""
##############################################
def __init__(self, position, default=None, key_parameter=False):
super().__init__(default)
self._position = position
self._key_parameter = key_parameter
##############################################
@property
def position(self):
return self._position
@property
def key_parameter(self):
return self._key_parameter
##############################################
def to_str(self, instance):
return str(self.__get__(instance))
##############################################
def __lt__(self, other):
return self._position < other.position
####################################################################################################
class ElementNamePositionalParameter(PositionalElementParameter):
"""This class implements an element name positional parameter."""
##############################################
def validate(self, value):
return str(value)
####################################################################################################
class ExpressionPositionalParameter(PositionalElementParameter):
"""This class implements an expression positional parameter. """
##############################################
def validate(self, value):
return str(value)
####################################################################################################
class FloatPositionalParameter(PositionalElementParameter):
"""This class implements a float positional parameter."""
##############################################
def validate(self, value):
if isinstance(value, Unit):
return value
else:
return Unit(value)
####################################################################################################
class InitialStatePositionalParameter(PositionalElementParameter):
"""This class implements an initial state (on, off) positional parameter."""
##############################################
def validate(self, value):
return bool(value) # Fixme: check KeyParameter
##############################################
def to__str_(self, instance):
if self.__get__(instance):
return 'on'
else:
return 'off'
####################################################################################################
class ModelPositionalParameter(PositionalElementParameter):
"""This class implements a model positional parameter. """
##############################################
def validate(self, value):
return str(value)
####################################################################################################
class FlagParameter(ParameterDescriptor):
"""This class implements a flag parameter.
Public Attributes:
:attr:`spice_name`
Name of the parameter
"""
##############################################
def __init__(self, spice_name, default=False):
super().__init__(default)
self.spice_name = spice_name
##############################################
def nonzero(self, instance):
return bool(self.__get__(instance))
##############################################
def to_str(self, instance):
if self.nonzero(instance):
return 'off'
else:
return ''
####################################################################################################
class KeyValueParameter(ParameterDescriptor):
"""This class implements a key value pair parameter.
Public Attributes:
:attr:`spice_name`
Name of the parameter
"""
##############################################
def __init__(self, spice_name, default=None):
super().__init__(default)
self.spice_name = spice_name
##############################################
def str_value(self, instance):
return str(self.__get__(instance))
##############################################
def to_str(self, instance):
if bool(self):
return '{}={}'.format(self.spice_name, self.str_value(instance))
else:
return ''
####################################################################################################
class BoolKeyParameter(KeyValueParameter):
"""This class implements a boolean key parameter."""
##############################################
def nonzero(self, instance):
return bool(self.__get__(instance))
##############################################
def str_value(self, instance):
if self.nonzero(instance):
return '1'
else:
return '0'
####################################################################################################
class ExpressionKeyParameter(KeyValueParameter):
"""This class implements an expression key parameter."""
##############################################
def validate(self, value):
return str(value)
####################################################################################################
class FloatKeyParameter(KeyValueParameter):
"""This class implements a float key parameter."""
##############################################
def validate(self, value):
return float(value)
####################################################################################################
class FloatPairKeyParameter(KeyValueParameter):
"""This class implements a float pair key parameter. """
##############################################
def validate(self, pair):
if len(pair) == 2:
return (float(pair[0]), float(pair[1]))
else:
raise ValueError()
##############################################
def str_value(self, instance):
return ','.join([str(value) for value in self.__get__(instance)])
####################################################################################################
class FloatTripletKeyParameter(FloatPairKeyParameter):
"""This class implements a triplet key parameter."""
##############################################
def validate(self, uplet):
if len(uplet) == 3:
return (float(uplet[0]), float(uplet[1]), float(uplet[2]))
else:
raise ValueError()
####################################################################################################
class IntKeyParameter(KeyValueParameter):
"""This class implements an integer key parameter."""
##############################################
def validate(self, value):
return int(value)
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/HighLevelElement.py 0000664 0000000 0000000 00000047073 13053355342 0021446 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
""" This module implements high level elements built on Spice elements. """
# Fixme: these waveforms can be current sources as well
####################################################################################################
from ..Math import rms_to_amplitude, amplitude_to_rms
from ..Tools.StringTools import join_list, join_dict
from ..Unit.Units import Frequency, Period
from .BasicElement import VoltageSource
####################################################################################################
class Sinusoidal(VoltageSource):
r""" This class implements a sinusoidal waveform.
+------+----------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+------+----------------+---------------+-------+
| Vo + offset + + V, A |
+------+----------------+---------------+-------+
| Va + amplitude + + V, A |
+------+----------------+---------------+-------+
| f + frequency + 1 / TStop + Hz |
+------+----------------+---------------+-------+
| Td + delay + 0.0 + sec |
+------+----------------+---------------+-------+
| Df + damping factor + 0.01 + 1/sec |
+------+----------------+---------------+-------+
The shape of the waveform is described by the following formula:
.. math::
V(t) = \begin{cases}
V_o & \text{if}\ 0 \leq t < T_d, \\
V_o + V_a e^{-D_f(t-T_d)} \sin\left(2\pi f (t-T_d)\right) & \text{if}\ T_d \leq t < T_{stop}.
\end{cases}
Spice syntax::
SIN ( Voffset Vamplitude Freq Tdelay DampingFactor )
Public Attributes:
:attr:`amplitude`
:attr:`damping_factor`
:attr:`dc_offset`
:attr:`delay`
:attr:`frequency`
:attr:`offset`
"""
##############################################
def __init__(self, name, node_plus, node_minus,
dc_offset=0,
offset=0, amplitude=1, frequency=50,
delay=0, damping_factor=0):
super().__init__(name, node_plus, node_minus)
self.dc_offset = dc_offset
self.offset = offset
self.amplitude = amplitude
self.frequency = Frequency(frequency) # Fixme: protect by setter?
self.delay = delay
self.damping_factor = damping_factor
##############################################
@property
def rms_voltage(self):
return amplitude_to_rms(self.amplitude)
##############################################
@property
def period(self):
return self.frequency.period
##############################################
def format_spice_parameters(self):
return join_list(('DC {}V'.format(self.dc_offset),
'AC SIN({}V {}V {}Hz {}s {})'.format(self.offset, self.amplitude,
self.frequency, self.delay,
self.damping_factor)))
####################################################################################################
class AcLine(Sinusoidal):
##############################################
def __init__(self, name, node_plus, node_minus, rms_voltage=230, frequency=50):
super().__init__(name, node_plus, node_minus,
amplitude=rms_to_amplitude(rms_voltage),
frequency=frequency)
####################################################################################################
class Pulse(VoltageSource):
"""This class implements a pulse waveform.
Nomenclature:
+--------+---------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+--------+---------------+---------------+-------+
| V1 + initial value + + V, A |
+--------+---------------+---------------+-------+
| V2 + pulsed value + + V, A |
+--------+---------------+---------------+-------+
| Td + delay time + 0.0 + sec |
+--------+---------------+---------------+-------+
| Tr + rise time + Tstep + sec |
+--------+---------------+---------------+-------+
| Tf + fall time + Tstep + sec |
+--------+---------------+---------------+-------+
| Pw + pulse width + Tstop + sec |
+--------+---------------+---------------+-------+
| Period + period + Tstop + sec |
+--------+---------------+---------------+-------+
Spice Syntax::
PULSE ( V1 V2 Td Tr Tf Pw Period )
A single pulse so specified is described by the following table:
+-------------+-------+
| Time | Value |
+-------------+-------+
| 0 | V1 |
+-------------+-------+
| Td | V1 |
+-------------+-------+
| Td+Tr | V2 |
+-------------+-------+
| Td+Tr+Pw | V2 |
+-------------+-------+
| Td+Tr+Pw+Tf | V1 |
+-------------+-------+
| Tstop | V1 |
+-------------+-------+
Note: default value in Spice for rise and fall time is the simulation transient step, pulse
width and period is the simulation stop time.
Public Attributes:
:attr:`delay_time`
:attr:`fall_time`
:attr:`initial_value`
:attr:`period`
:attr:`pulse_width`
:attr:`pulsed_value`
:attr:`rise_time`
"""
##############################################
def __init__(self, name, node_plus, node_minus,
initial_value, pulsed_value,
pulse_width, period,
delay_time=0, rise_time=0, fall_time=0):
# Fixme: default
# rise_time, fall_time = Tstep
# pulse_width, period = Tstop
super().__init__(name, node_plus, node_minus)
self.initial_value = initial_value
self.pulsed_value = pulsed_value
self.delay_time = delay_time
self.rise_time = rise_time
self.fall_time = fall_time
self.pulse_width = pulse_width
self.period = Period(period) # Fixme: protect by setter?
# # Fixme: to func?
# # Check parameters
# found_none = False
# for parameter in ('rise_time', 'fall_time', 'pulse_width', 'period'):
# parameter_value = getattr(self, parameter)
# if found_none:
# if parameter_value is not None:
# raise ValueError("Parameter {} is set but some previous parameters was not set".format(parameter))
# else:
# found_none = parameter_value is None
##############################################
@property
def frequency(self):
return self.period.frequency
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('PULSE(' +
join_list((self.initial_value, self.pulsed_value, self.delay_time,
self.rise_time, self.fall_time, self.pulse_width, self.period)) +
')')
####################################################################################################
class Exponential(VoltageSource):
r"""This class implements a Exponential waveform.
Nomenclature:
+------+--------------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+------+--------------------+---------------+-------+
| V1 + Initial value + + V, A |
+------+--------------------+---------------+-------+
| V2 + pulsed value + + V, A |
+------+--------------------+---------------+-------+
| Td1 + rise delay time + 0.0 + sec |
+------+--------------------+---------------+-------+
| tau1 + rise time constant + Tstep + sec |
+------+--------------------+---------------+-------+
| Td2 + fall delay time + Td1+Tstep + sec |
+------+--------------------+---------------+-------+
| tau2 + fall time constant + Tstep + sec |
+------+--------------------+---------------+-------+
Spice Syntax::
EXP ( V1 V2 TD1 TAU1 TD2 TAU2 )
The shape of the waveform is described by the following formula:
Let V21 = V2 - V1 and V12 = V1 - V2.
.. math::
V(t) = \begin{cases}
V_1 & \text{if}\ 0 \leq t < T_{d1}, \\
V_1 + V_{21} ( 1 − e^{-\frac{t-T_{d1}}{\tau_1}} )
& \text{if}\ T_{d1} \leq t < T_{d2}, \\
V_1 + V_{21} ( 1 − e^{-\frac{t-T_{d1}}{\tau_1}} ) + V_{12} ( 1 − e^{-\frac{t-T_{d2}}{\tau_2}} )
& \text{if}\ T_{d2} \leq t < T_{stop}
\end{cases}
"""
##############################################
def __init__(self, name, node_plus, node_minus,
initial_value, pulsed_value,
rise_delay_time=.0, rise_time_constant=None,
fall_delay_time=None, fall_time_constant=None,
):
# Fixme: default
super().__init__(name, node_plus, node_minus)
self.initial_value = initial_value
self.pulsed_value = pulsed_value
self.rise_delay_time = rise_delay_time
self.rise_time_constant = rise_time_constant
self.fall_delay_time = fall_delay_time
self.fall_time_constant = fall_time_constant
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('EXP(' +
join_list((self.initial_value, self.pulsed_value,
self.rise_delay_time, self.rise_time_constant,
self.fall_delay_time, self.fall_time_constant,
)) +
')')
####################################################################################################
class PieceWiseLinear(VoltageSource):
r"""This class implements a Piece-Wise Linear waveform.
Spice Syntax::
PWL( T1 V1 )
Each pair of values (Ti , Vi) specifies that the value of the source is Vi (in Volts or Amps) at
time = Ti . The value of the source at intermediate values of time is determined by using linear
interpolation on the input values. The parameter r determines a repeat time point. If r is not
given, the whole sequence of values (Ti , Vi ) is issued once, then the output stays at its
final value. If r = 0, the whole sequence from time = 0 to time = Tn is repeated forever. If r =
10ns, the sequence between 10ns and 50ns is repeated forever. the r value has to be one of the
time points T1 to Tn of the PWL sequence. If td is given, the whole PWL sequence is delayed by a
delay time time = td. The current source still needs to be patched, td and r are not yet
available.
"""
##############################################
def __init__(self, name, node_plus, node_minus,
values,
repeate_time=0, delay_time=.0,
):
# Fixme: default
super().__init__(name, node_plus, node_minus)
self.values = values
self.repeate_time = repeate_time
self.delay_time = delay_time
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('PWL(' +
join_list(self.values) +
join_dict({'r':self.repeate_time, 'td':self.delay_time}) + # OrderedDict(
')')
####################################################################################################
class SingleFrequencyFM(VoltageSource):
r"""This class implements a Single-Frequency FM waveform.
Spice Syntax::
SFFM (VO VA FC MDI FS )
+------+-------------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+------+-------------------+---------------+-------+
| Vo + offset + + V, A |
+------+-------------------+---------------+-------+
| Va + amplitude + + V, A |
+------+-------------------+---------------+-------+
| Fc + carrier frequency + 1 / Tstop + Hz |
+------+-------------------+---------------+-------+
| Mdi + modulation index + + |
+------+-------------------+---------------+-------+
| Fs + signal frequency + 1 / Tstop + Hz |
+------+-------------------+---------------+-------+
The shape of the waveform is described by the following equation:
.. math::
V(t) = V_o + V_a \sin (2\pi F_c\, t + M_{di} \sin (2\pi F_s\,t))
"""
##############################################
def __init__(self, name, node_plus, node_minus,
offset, amplitude, carrier_frequency, modulation_index, signal_frequency):
super().__init__(name, node_plus, node_minus)
self.offset = offset
self.amplitude = amplitude
self.carrier_frequency = Frequency(carrier_frequency)
self.modulation_index = modulation_index
self.signal_frequency = Frequency(signal_frequency)
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('SFFM(' +
join_list((self.offset, self.amplitude, self.carrier_frequency,
self.modulation_index, self.signal_frequency)) +
')')
####################################################################################################
class AmplitudeModulated(VoltageSource):
r"""This class implements a Amplitude Modulated source.
+------+----------------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+------+----------------------+---------------+-------+
| Vo + offset + + V, A |
+------+----------------------+---------------+-------+
| Va + amplitude + + V, A |
+------+----------------------+---------------+-------+
| Mf + modulating frequency + + Hz |
+------+----------------------+---------------+-------+
| Fc + carrier frequency + 1 / Tstop + Hz |
+------+----------------------+---------------+-------+
| Td + signal delay + + s |
+------+----------------------+---------------+-------+
Spice Syntax::
AM(VA VO MF FC TD)
The shape of the waveform is described by the following equation:
.. math::
V(t) = V_a (V_o + \sin (2\pi M_f\,t)) \sin (2\pi F_c\,t)
"""
##############################################
def __init__(self, name, node_plus, node_minus,
offset, amplitude, modulating_frequency, carrier_frequency, signal_delay):
# Fixme: default
super().__init__(name, node_plus, node_minus)
self.offset = offset
self.amplitude = amplitude
self.carrier_frequency = Frequency(carrier_frequency)
self.modulating_frequency = Frequency(modulating_frequency)
self.signal_delay = signal_delay
##############################################
def format_spice_parameters(self):
# Fixme: to func?
return ('AM(' +
join_list((self.offset, self.amplitude, self.carrier_frequency,
self.modulating_frequency, self.signal_delay)) +
')')
####################################################################################################
class RandomVoltage(VoltageSource):
r"""This class implements a Random Voltage source.
The TRRANDOM option yields statistically distributed voltage values, derived from the ngspice
random number generator. These values may be used in the transient simulation directly within a
circuit, e.g. for generating a specific noise voltage, but especially they may be used in the
control of behavioral sources (B, E, G sources, voltage controllable A sources, capacitors,
inductors, or resistors) to simulate the circuit dependence on statistically varying device
parameters. A Monte-Carlo simulation may thus be handled in a single simulation run.
Spice Syntax::
TRRANDOM( TYPE TS
> >)
TYPE determines the random variates generated: 1 is uniformly distributed, 2 Gaussian, 3
exponential, 4 Poisson. TS is the duration of an individual voltage value. TD is a time delay
with 0 V output before the random voltage values start up. PARAM1 and PARAM2 depend on the type
selected.
+-------------+---------------+---------+-------------+---------+
| Type + Parameter 1 + Default + Parameter 2 + Default |
+-------------+---------------+---------+-------------+---------+
| uniform + range + 1 + offset + 0 |
+-------------+---------------+---------+-------------+---------+
| gaussian + standard dev. + 1 + mean + 0 |
+-------------+---------------+---------+-------------+---------+
| exponential + mean + 1 + offset + 0 |
+-------------+---------------+---------+-------------+---------+
| poisson + lambda + 1 + offset + 0 |
+-------------+---------------+---------+-------------+---------+
"""
##############################################
def __init__(self, name, node_plus, node_minus,
random_type, duration=0, time_delay=0, parameter1=1, parameter2=0):
# Fixme: random_type and parameters
super().__init__(name, node_plus, node_minus)
self.random_type = random_type
self.duration = duration
self.time_delay = time_delay
self.parameter1 = parameter1
self.parameter2 = parameter2
##############################################
def format_spice_parameters(self):
if self.random_type == 'uniform':
random_type = 1
elif self.random_type == 'exponential':
random_type = 2
elif self.random_type == 'gaussian':
random_type = 3
elif self.random_type == 'poisson':
random_type = 4
else:
raise ValueError("Wrong random type {}".format(self.random_type))
# Fixme: to func?
return ('TRRANDOM(' +
join_list((random_type, self.duration, self.time_delay,
self.parameter1, self.parameter2)) +
')')
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/Library.py 0000664 0000000 0000000 00000007343 13053355342 0017665 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import logging
####################################################################################################
from ..Tools.File import Directory
from .Parser import SpiceParser
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class SpiceLibrary:
"""This class implements a Spice sub-circuits and models library.
A library is a directory which is recursively scanned for '.lib' file and parsed for sub-circuit
and models definitions.
Example of usage::
spice_library = SpiceLibrary('/some/path/')
If the directory hierarchy contains a file that define a 1N4148 sub-circuit then we can retrieve
the file path using::
spice_library['1N4148']
"""
_logger = _module_logger.getChild('Library')
##############################################
def __init__(self, root_path):
self._directory = Directory(root_path).expand_vars_and_user()
self._subcircuits = {}
self._models = {}
for path in self._directory.iter_file():
if path.extension.lower() in ('.lib', '.mod'):
self._logger.debug("Parse {}".format(path))
spice_parser = SpiceParser(path)
if spice_parser.is_only_subcircuit():
for subcircuit in spice_parser.subcircuits:
self._subcircuits[subcircuit.name] = path
elif spice_parser.is_only_model():
for model in spice_parser.models:
self._models[model.name] = path
##############################################
def __getitem__(self, name):
if name in self._subcircuits:
return self._subcircuits[name]
elif name in self._models:
return self._models[name]
else:
raise KeyError(name)
##############################################
@property
def subcircuits(self):
""" Dictionary of sub-circuits """
return iter(self._subcircuits)
@property
def models(self):
""" Dictionary of models """
return iter(self._models)
# ##############################################
# def iter_on_subcircuits(self):
# return self._subcircuits.itervalues()
# ##############################################
# def iter_on_models(self):
# return self._models.itervalues()
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/Netlist.py 0000664 0000000 0000000 00000067152 13053355342 0017707 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This modules implements circuit and subcircuit.
The definition of a netlist follows the same conventions as SPICE. For example this SPICE netlist
is translated to Python like this:
.. code-block:: spice
.title Voltage Divider
Vinput in 0 10V
R1 in out 9k
R2 out 0 1k
.end
.. code-block:: py
circuit = Circuit('Voltage Divider')
circuit.V('input', 'in', circuit.gnd, 10)
circuit.R(1, 'in', 'out', kilo(9))
circuit.R(2, 'out', circuit.gnd, kilo(1))
or as a class definition:
.. code-block:: py
class VoltageDivider(Circuit):
def __init__(self, **kwargs):
super().__init__(title='Voltage Divider', **kwargs)
self.V('input', 'in', self.gnd, '10V')
self.R(1, 'in', 'out', kilo(9))
self.R(2, 'out', self.gnd, kilo(1))
The circuit attribute :attr:`gnd` represents the ground of the circuit or subcircuit, usually set to
0.
We can get an element or a model using its name using these two possibilities::
circuit['R1'] # dictionnary style
circuit.R1 # attribute style
The dictionnary style always works, but the attribute only works if it complies with the Python
syntax, i.e. the element or model name is a valide attribute name (identifier), i.e. starting by a
letter and not a keyword like 'in', cf. `Python Language Reference
`_.
We can update an element parameter like this::
circuit.R1.resistance = kilo(1)
To simulate the circuit, we must create a simulator instance using the :meth:`Circuit.simulator`::
simulator = circuit.simulator()
"""
####################################################################################################
#
# Graph:
# dipole
# n-pole: transistor (be, bc) ?
#
# circuit -> element -> node
# circuit.Q1.b
# Element -> ElementQ
# use prefix?
#
####################################################################################################
####################################################################################################
from collections import OrderedDict
import keyword
import logging
import os
# import networkx
####################################################################################################
from ..Tools.StringTools import join_lines, join_list, join_dict
from .ElementParameter import (ParameterDescriptor,
PositionalElementParameter,
FlagParameter, KeyValueParameter)
from .Simulation import SubprocessCircuitSimulator, NgSpiceSharedCircuitSimulator
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class DeviceModel:
""" This class implements a device model.
Ngspice model types:
+------+-------------------------------+
| Code + Model Type |
+------+-------------------------------+
| R + Semiconductor resistor model |
+------+-------------------------------+
| C + Semiconductor capacitor model |
+------+-------------------------------+
| L + Inductor model |
+------+-------------------------------+
| SW + Voltage controlled switch |
+------+-------------------------------+
| CSW + Current controlled switch |
+------+-------------------------------+
| URC + Uniform distributed RC model |
+------+-------------------------------+
| LTRA + Lossy transmission line model |
+------+-------------------------------+
| D + Diode model |
+------+-------------------------------+
| NPN + NPN BJT model |
+------+-------------------------------+
| PNP + PNP BJT model |
+------+-------------------------------+
| NJF + N-channel JFET model |
+------+-------------------------------+
| PJF + P-channel JFET model |
+------+-------------------------------+
| NMOS + N-channel MOSFET model |
+------+-------------------------------+
| PMOS + P-channel MOSFET model |
+------+-------------------------------+
| NMF + N-channel MESFET model |
+------+-------------------------------+
| PMF + P-channel MESFET model |
+------+-------------------------------+
"""
##############################################
def __init__(self, name, modele_type, **parameters):
self._name = str(name)
self._model_type = str(modele_type)
self._parameters = dict(**parameters)
##############################################
@property
def name(self):
return self._name
##############################################
@property
def model_type(self):
return self._model_type
##############################################
def __repr__(self):
return str(self.__class__) + ' ' + self.name
##############################################
def __str__(self):
return ".model {} {} ({})".format(self._name, self._model_type, join_dict(self._parameters))
####################################################################################################
class Pin:
"""This class implements a pin of an element. It stores a reference to the element, the name of the
pin and the node.
"""
_logger = _module_logger.getChild('Pin')
##############################################
def __init__(self, element, name, node):
if keyword.iskeyword(node):
self._logger.warning("Node {} is a Python keyword".format(node))
self._element = element
self._name = name
self._node = node # Fixme: name, not a Node instance, cf. Netlist.nodes
##############################################
@property
def element(self):
return self._element
##############################################
@property
def name(self):
return self._name
##############################################
@property
def node(self):
return self._node
##############################################
def __repr__(self):
return "Pin {} of {} on node {}".format(self._name, self._element.name, self._node)
##############################################
def add_current_probe(self, circuit):
"""Add a current probe between the node and the pin.
The ammeter is named *ElementName_PinName*.
"""
# Fixme: require a reference to circuit
# Fixme: add it to a list
node = self._node
self._node = '_'.join((self._element.name, self._name))
circuit.V(self._node, node, self._node, '0')
####################################################################################################
class ElementParameterMetaClass(type):
""" Metaclass to implements the element parameter machinery. """
__classes__ = {}
##############################################
def __new__(cls, class_name, super_classes, attributes):
# Collect positional and optional parameters from class attribute dict
positional_parameters = {}
parameters = {}
for attribute_name, obj in attributes.items():
if isinstance(obj, ParameterDescriptor):
obj.attribute_name = attribute_name
if isinstance(obj, PositionalElementParameter):
d = positional_parameters
elif isinstance(obj, (FlagParameter, KeyValueParameter)):
d = parameters
# else:
# raise NotImplementedError
d[attribute_name] = obj
# Build dictionnary : attribute_name -> parameter
attributes['_positional_parameters'] = OrderedDict(sorted(list(positional_parameters.items()),
key=lambda t: t[1].position))
# optional parameter order is not required for SPICE, but for unit test
attributes['_optional_parameters'] = OrderedDict(sorted(list(parameters.items()), key=lambda t: t[0]))
# Positional parameter array
attributes['_parameters_from_args'] = [parameter
for parameter in sorted(positional_parameters.values())
if not parameter.key_parameter]
# Implement alias for parameters
attributes['_spice_to_parameters'] = {parameter.spice_name:parameter
for parameter in attributes['_optional_parameters'].values()}
for parameter in attributes['_spice_to_parameters'].values():
if (parameter.spice_name in attributes
and parameter.spice_name != parameter.attribute_name):
_module_logger.error('Spice parameter "{}" clash with attributes'.format(parameter.spice_name))
return super().__new__(cls, class_name, super_classes, attributes)
##############################################
def __init__(cls, class_name, super_classes, attributes):
type.__init__(cls, class_name, super_classes, attributes)
# Collect basic element classes
if 'prefix' in attributes:
prefix = attributes['prefix']
if prefix is not None:
classes = ElementParameterMetaClass.__classes__
if prefix in classes:
classes[prefix].append(cls)
else:
classes[prefix] = [cls]
##############################################
# Notes: These properties are only accessible from the class object, e.g. instance.__class__
@property
def number_of_pins(cls):
return cls._number_of_pins
@property
def number_of_positional_parameters(cls):
return len(cls._positional_parameters)
@property
def positional_parameters(cls):
return cls._positional_parameters
@property
def optional_parameters(cls):
return cls._optional_parameters
@property
def parameters_from_args(cls):
return cls._parameters_from_args
@property
def spice_to_parameters(cls):
return cls._spice_to_parameters
####################################################################################################
class Element(metaclass=ElementParameterMetaClass):
""" This class implements a base class for an element.
It use a metaclass machinery for the declaration of the parameters.
"""
# These attributes are defined in subclasses or via the metaclass.
_number_of_pins = None
_positional_parameters = None
_optional_parameters = None
_parameters_from_args = None
_spice_to_parameters = None
# Fixme: _prefix
#: SPICE element prefix
prefix = None
##############################################
def __init__(self, name, pins, *args, **kwargs):
self._name = str(name)
self._pins = list(pins) # Fixme: pins is not a ordered dict, cf. property
# self._parameters = list(args)
for parameter, value in zip(self._parameters_from_args, args):
setattr(self, parameter.attribute_name, value)
for key, value in kwargs.items():
if key in self._positional_parameters or self._optional_parameters:
setattr(self, key, value)
##############################################
@property
def name(self):
return self.prefix + self._name
@property
def pins(self):
return self._pins
##############################################
@property
def nodes(self):
return [pin.node for pin in self._pins]
##############################################
def __repr__(self):
return self.__class__.__name__ + ' ' + self.name
##############################################
def __setattr__(self, name, value):
# Implement alias for parameters
if name in self._spice_to_parameters:
parameter = self._spice_to_parameters[name]
object.__setattr__(self, parameter.attribute_name, value)
else:
object.__setattr__(self, name, value)
##############################################
def __getattr__(self, name):
# Implement alias for parameters
if name in self._spice_to_parameters:
parameter = self._spice_to_parameters[name]
return object.__getattribute__(self, parameter.attribute_name)
else:
raise AttributeError(name)
##############################################
def format_node_names(self):
""" Return the formatted list of nodes. """
return join_list((self.name, join_list(self.nodes)))
##############################################
def parameter_iterator(self):
""" This iterator returns the parameter in the right order. """
for parameter_dict in self._positional_parameters, self._optional_parameters:
for parameter in parameter_dict.values():
if parameter.nonzero(self):
yield parameter
##############################################
# @property
# def parameters(self):
# return self._parameters
##############################################
def format_spice_parameters(self):
""" Return the formatted list of parameters. """
return join_list([parameter.to_str(self) for parameter in self.parameter_iterator()])
##############################################
def __str__(self):
""" Return the SPICE element definition. """
return join_list((self.format_node_names(), self.format_spice_parameters()))
####################################################################################################
class NPinElement(Element):
pass
####################################################################################################
class AnyPinElement(Element):
_number_of_pins = 0
####################################################################################################
class TwoPinElement(Element):
""" This class implements a base class for a two-pin element. """
# dipole
_number_of_pins = 2
##############################################
def __init__(self, name, node_plus, node_minus, *args, **kwargs):
pins = (Pin(self, 'plus', node_plus), Pin(self, 'minus', node_minus))
super().__init__(name, pins, *args, **kwargs)
##############################################
@property
def plus(self):
return self.pins[0]
##############################################
@property
def minus(self):
return self.pins[1]
####################################################################################################
class ThreePinElement(Element):
_number_of_pins = 3
####################################################################################################
class FourPinElement(Element):
_number_of_pins = 4
####################################################################################################
class TwoPortElement(Element):
""" This class implements a base class for a two-port element.
.. warning:: As opposite to Spice, the input nodes are specified before the output nodes.
"""
_number_of_pins = 4
##############################################
# Fixme: Why the order the inverted ?
def __init__(self, name,
input_node_plus, input_node_minus,
output_node_plus, output_node_minus,
*args, **kwargs):
pins = (Pin(self, 'output_plus', output_node_plus),
Pin(self, 'output_minus', output_node_minus),
Pin(self, 'input_plus', input_node_plus),
Pin(self, 'input_minus', input_node_minus))
super().__init__(name, pins, *args, **kwargs)
##############################################
@property
def output_plus(self):
return self.pins[0]
##############################################
@property
def output_minus(self):
return self.pins[1]
##############################################
@property
def input_plus(self):
return self.pins[2]
##############################################
@property
def input_minus(self):
return self.pins[3]
####################################################################################################
class Node:
"""This class implements a node in the circuit. It stores a reference to the elements connected to
the node."""
# Fixme: but not directly to the pins!
##############################################
def __init__(self, name):
self._name = str(name)
self._elements = set()
##############################################
def __repr__(self):
return 'Node {}'.format(self._name)
##############################################
@property
def name(self):
return self._name
@property
def elements(self):
return self._elements
##############################################
def add_element(self, element):
self._elements.add(element)
####################################################################################################
class Netlist:
""" This class implements a base class for a netlist.
.. note:: This class is completed at running time with elements.
"""
##############################################
def __init__(self):
self._ground = None
self._elements = OrderedDict() # to keep the declaration order
self._models = {}
self._dirty = True
# self._nodes = set()
self._nodes = {}
# self._graph = networkx.Graph()
##############################################
def element_iterator(self):
return iter(self._elements.values())
##############################################
def element_names(self):
return [element.name for element in self.element_iterator()]
##############################################
def model_iterator(self):
return iter(self._models.values())
##############################################
def __str__(self):
""" Return the formatted list of element and model definitions. """
netlist = join_lines(self.element_iterator()) + '\n'
if self._models:
netlist += join_lines(self.model_iterator()) + '\n'
return netlist
##############################################
def _add_element(self, element):
"""Add an element."""
if element.name not in self._elements:
self._elements[element.name] = element
self._dirty = True
else:
raise NameError("Element name {} is already defined".format(element.name))
##############################################
def model(self, name, modele_type, **parameters):
"""Add a model."""
model = DeviceModel(name, modele_type, **parameters)
if model.name not in self._models:
self._models[model.name] = model
else:
raise NameError("Model name {} is already defined".format(name))
##############################################
@property
def nodes(self):
"""Return the nodes."""
if self._dirty:
# nodes = set()
# for element in self.element_iterator():
# nodes |= set(element.nodes)
# if self._ground is not None:
# nodes -= set((self._ground,))
# self._nodes = nodes
self._nodes.clear()
for element in self.element_iterator():
for node_name in element.nodes:
if node_name not in self._nodes:
node = Node(node_name)
self._nodes[node_name] = node
else:
node = self._nodes[node_name]
node.add_element(element)
return list(self._nodes.values())
##############################################
def node_names(self):
return [node.name for node in self.nodes]
##############################################
def __getitem__(self, attribute_name):
if attribute_name in self._elements:
return self._elements[attribute_name]
elif attribute_name in self._models:
return self._models[attribute_name]
elif attribute_name in self._nodes:
return attribute_name
else:
raise IndexError(attribute_name)
##############################################
def __getattr__(self, attribute_name):
try:
return self.__getitem__(attribute_name)
except IndexError:
raise AttributeError(attribute_name)
####################################################################################################
class SubCircuit(Netlist):
"""This class implements a sub-cicuit netlist."""
##############################################
def __init__(self, name, *nodes, **kwargs):
super().__init__()
self.name = str(name)
self._external_nodes = list(nodes)
# Fixme: ok ?
self._ground = kwargs.get('ground', 0)
if 'ground' in kwargs:
del kwargs['ground']
self._parameters = kwargs
##############################################
@property
def gnd(self):
""" Local ground """
return self._ground
##############################################
@property
def parameters(self):
"""Parameters"""
return self._parameters
##############################################
def check_nodes(self):
"""Check for dangling nodes in the subcircuit."""
nodes = set(self._external_nodes)
connected_nodes = set()
for element in self.element_iterator():
connected_nodes.add(nodes & element.nodes)
not_connected_nodes = nodes - connected_nodes
if not_connected_nodes:
raise NameError("SubCircuit Nodes {} are not connected".format(not_connected_nodes))
##############################################
def __str__(self):
"""Return the formatted subcircuit definition."""
nodes = join_list(self._external_nodes)
parameters = join_list(['{}={}'.format(key, value)
for key, value in self._parameters.items()])
netlist = '.subckt ' + join_list((self.name, nodes, parameters)) + '\n'
netlist += super().__str__()
netlist += '.ends ' + self.name + '\n'
return netlist
####################################################################################################
class SubCircuitFactory(SubCircuit):
# Fixme : versus SubCircuit
__name__ = None
__nodes__ = None
##############################################
def __init__(self, **kwargs):
super().__init__(self.__name__, *self.__nodes__, **kwargs)
####################################################################################################
class Circuit(Netlist):
"""This class implements a cicuit netlist.
To get the corresponding Spice netlist use::
circuit = Circuit()
...
str(circuit)
"""
# .lib
# .func
# .csparam
##############################################
def __init__(self, title,
ground=0,
global_nodes=(),
):
super().__init__()
self.title = str(title)
self._ground = ground
self._global_nodes = set(global_nodes) # .global
self._includes = [] # .include
self._parameters = {} # .param
self._subcircuits = {}
# Fixme: not implemented
# .func
# .csparam
# .if
##############################################
@property
def gnd(self):
return self._ground
##############################################
def include(self, path):
"""Include a file."""
if path not in self._includes:
self._includes.append(path)
else:
self._logger.warn("Duplicated include")
##############################################
def parameter(self, name, expression):
"""Set a parameter."""
self._parameters[str(name)] = str(expression)
##############################################
def subcircuit(self, subcircuit):
"""Add a sub-circuit."""
self._subcircuits[str(subcircuit.name)] = subcircuit
##############################################
def subcircuit_iterator(self):
"""Return a sub-circuit iterator."""
return iter(self._subcircuits.values())
##############################################
def __str__(self):
"""Return the formatted desk."""
netlist = '.title {}\n'.format(self.title)
if self._includes:
# ngspice don't like // in path, thus ensure we write real paths
real_paths = [os.path.realpath(str(path)) for path in self._includes]
netlist += join_lines(real_paths, prefix='.include ') + '\n'
if self._global_nodes:
netlist += '.global ' + join_list(self._global_nodes) + '\n'
if self._parameters:
netlist += join_lines(self._parameters, prefix='.param ') + '\n'
if self._subcircuits:
netlist += join_lines(self.subcircuit_iterator())
netlist += super().__str__()
return netlist
##############################################
def str_end(self):
return str(self) + '.end\n'
##############################################
def simulator(self, *args, **kwargs):
"""Return a :obj:`PySpice.Spice.Simulation.SubprocessCircuitSimulator` or
:obj:`PySpice.Spice.Simulation.NgSpiceSharedCircuitSimulator` instance depending of the
value of the *simulator* parameter: ``subprocess`` or ``shared``, respectively. If this
parameter is not specified then a subprocess simulator is returned.
"""
simulator = kwargs.get('simulator', 'subprocess')
if 'simulator' in kwargs:
simulator = kwargs['simulator']
del kwargs['simulator']
else:
simulator = 'subprocess'
if simulator == 'subprocess':
return SubprocessCircuitSimulator(self, *args, **kwargs)
elif simulator == 'shared':
return NgSpiceSharedCircuitSimulator(self, *args, **kwargs)
else:
return ValueError('Unknown simulator type')
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/NgSpice/ 0000775 0000000 0000000 00000000000 13053355342 0017230 5 ustar 00root root 0000000 0000000 PySpice-v0.3.2/PySpice/Spice/NgSpice/Shared.py 0000664 0000000 0000000 00000051421 13053355342 0021013 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This module provides a Python interface to the Ngspice shared library described in the *ngspice
as shared library or dynamic link library* section of the Ngspice user manual.
In comparison to the subprocess interface, it provides an interaction with the simulator through
commands and callbacks and it enables the usage of external voltage and current source in the
circuit.
.. This approach corresponds to the *standard way* to make an interface to a simulator code.
.. warning:: Since we don't simulate a circuit in a fresh environment on demand, this approach is
less safe than the subprocess approach. In case of bugs within Ngspice, we can expect some side
effects like memory leaks or worse unexpected things.
This interface use the CFFI module to interface with the shared library. It is thus suited to run
within the Pypy interpreter which implements JIT optimisation for Python.
It can also be used to experiment parallel simulation as explained in the Ngspice user manual. But
it seems the Ngspice source code was designed with global variables which imply to use one copy of
the shared library by worker as explained in the manual.
.. warning:: This interface can strongly slow down the simulation if the input or output callbacks
is used. If the simulation time goes wrong for you then you need to implement the callbacks at a
lower level than Python. You can have look to Pypy, Cython or a C extension module.
"""
####################################################################################################
import logging
import os
import time
import numpy as np
####################################################################################################
from cffi import FFI
ffi = FFI()
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
from PySpice.Probe.WaveForm import (OperatingPoint, SensitivityAnalysis,
DcAnalysis, AcAnalysis, TransientAnalysis,
WaveForm)
from PySpice.Tools.EnumFactory import EnumFactory
####################################################################################################
def ffi_string_utf8(x):
return ffi.string(x).decode('utf8')
####################################################################################################
class Vector:
""" This class implements a vector in a simulation output.
Public Attributes:
:attr:`data`
:attr:`name`
:attr:`type`
cf. `NgSpiceShared.simulation_type`
"""
##############################################
def __init__(self, name, type_, data):
self.name = str(name)
self.type = type_
self.data = data
##############################################
def __repr__(self):
return 'variable: {0.name} {0.type}'.format(self)
##############################################
def is_voltage_node(self):
return self.type == NgSpiceShared.simulation_type.voltage
##############################################
def is_branch_current(self):
return self.type == NgSpiceShared.simulation_type.current
##############################################
@property
def simplified_name(self):
if self.is_branch_current():
# return self.name.replace('#branch', '')
return self.name[:-7]
else:
return self.name
##############################################
@property
def unit(self):
if self.type == NgSpiceShared.simulation_type.voltage:
return 'V'
elif self.type == NgSpiceShared.simulation_type.current:
return 'A'
elif self.type == NgSpiceShared.simulation_type.time:
return 's'
elif self.type == NgSpiceShared.simulation_type.frequency:
return 'Hz'
else:
return ''
##############################################
def to_waveform(self, abscissa=None, to_real=False, to_float=False):
""" Return a :obj:`PySpice.Probe.WaveForm` instance. """
data = self.data
if to_real:
data = data.real
if to_float:
data = float(data[0])
return WaveForm(self.simplified_name, self.unit, data, abscissa=abscissa)
####################################################################################################
class Plot(dict):
""" This class implements a plot in a simulation output.
Public Attributes:
:attr:`plot_name`
"""
##############################################
def __init__(self, plot_name):
super().__init__()
self.plot_name = plot_name
##############################################
def nodes(self, to_float=False, abscissa=None):
return [variable.to_waveform(abscissa, to_float=to_float)
for variable in self.values()
if variable.is_voltage_node()]
##############################################
def branches(self, to_float=False, abscissa=None):
return [variable.to_waveform(abscissa, to_float=to_float)
for variable in self.values()
if variable.is_branch_current()]
##############################################
def elements(self, abscissa=None):
return [variable.to_waveform(abscissa, to_float=True)
for variable in self.values()]
##############################################
def to_analysis(self):
if self.plot_name.startswith('op'):
return self._to_operating_point_analysis()
elif self.plot_name.startswith('sens'):
return self._to_sensitivity_analysis()
elif self.plot_name.startswith('dc'):
return self._to_dc_analysis()
elif self.plot_name.startswith('ac'):
return self._to_ac_analysis()
elif self.plot_name.startswith('tran'):
return self._to_transient_analysis()
else:
raise NotImplementedError("Unsupported plot name {}".format(self.plot_name))
##############################################
def _to_operating_point_analysis(self):
return OperatingPoint(nodes=self.nodes(to_float=True), branches=self.branches(to_float=True))
##############################################
def _to_sensitivity_analysis(self):
# Fixme: separate v(vinput), analysis.R2.m
return SensitivityAnalysis(elements=self.elements())
##############################################
def _to_dc_analysis(self):
if 'v(v-sweep)' in self:
sweep_variable = self['v(v-sweep)']
elif 'v(i-sweep)' in self:
sweep_variable = self['v(i-sweep)']
else:
raise NotImplementedError
sweep = sweep_variable.to_waveform()
return DcAnalysis(sweep, nodes=self.nodes(), branches=self.branches())
##############################################
def _to_ac_analysis(self):
frequency = self['frequency'].to_waveform(to_real=True)
return AcAnalysis(frequency, nodes=self.nodes(), branches=self.branches())
##############################################
def _to_transient_analysis(self):
time = self['time'].to_waveform(to_real=True)
return TransientAnalysis(time, nodes=self.nodes(abscissa=time), branches=self.branches(abscissa=time))
####################################################################################################
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
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/NgSpice/__init__.py 0000664 0000000 0000000 00000001627 13053355342 0021347 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/NgSpice/api.h 0000664 0000000 0000000 00000003433 13053355342 0020155 0 ustar 00root root 0000000 0000000 /* Simplified Ngspice API for CFFI parser */
typedef struct ngcomplex
{
double cx_real;
double cx_imag;
} ngcomplex_t;
typedef struct vector_info
{
char *v_name;
int v_type;
short v_flags;
double *v_realdata;
ngcomplex_t *v_compdata;
int v_length;
} vector_info, *pvector_info;
typedef struct vecvalues
{
char *name;
double creal;
double cimag;
bool is_scale;
bool is_complex;
} vecvalues, *pvecvalues;
typedef struct vecvaluesall
{
int veccount;
int vecindex;
pvecvalues *vecsa;
} vecvaluesall, *pvecvaluesall;
typedef struct vecinfo
{
int number;
char *vecname;
bool is_real;
void *pdvec;
void *pdvecscale;
} vecinfo, *pvecinfo;
typedef struct vecinfoall
{
char *name;
char *title;
char *date;
char *type;
int veccount;
pvecinfo *vecs;
} vecinfoall, *pvecinfoall;
typedef int (SendChar) (char *, int, void *);
typedef int (SendStat) (char *, int, void *);
typedef int (ControlledExit) (int, bool, bool, int, void *);
typedef int (SendData) (pvecvaluesall, int, int, void *);
typedef int (SendInitData) (pvecinfoall, int, void *);
typedef int (BGThreadRunning) (bool, int, void *);
typedef int (GetVSRCData) (double *, double, char *, int, void *);
typedef int (GetISRCData) (double *, double, char *, int, void *);
typedef int (GetSyncData) (double, double *, double, int, int, int, void *);
int ngSpice_Init (SendChar *, SendStat *, ControlledExit *, SendData *, SendInitData *, BGThreadRunning *, void *);
int ngSpice_Init_Sync (GetVSRCData *, GetISRCData *, GetSyncData *, int *, void *);
int ngSpice_Command (char *);
pvector_info ngGet_Vec_Info (char *);
int ngSpice_Circ (char **);
char *ngSpice_CurPlot (void);
char **ngSpice_AllPlots (void);
char **ngSpice_AllVecs (char *);
bool ngSpice_running (void);
bool ngSpice_SetBkpt (double);
/* End */
PySpice-v0.3.2/PySpice/Spice/Parser.py 0000664 0000000 0000000 00000056216 13053355342 0017520 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
"""This module implements a partial SPICE netlist parser.
It would be difficult to implement a full parser for Ngspice since the syntax is mainly contextual.
"""
####################################################################################################
import logging
####################################################################################################
from .ElementParameter import (
FlagParameter,
)
from .Netlist import ElementParameterMetaClass, NPinElement, Circuit
from .BasicElement import SubCircuitElement, BipolarJunctionTransistor
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class PrefixData:
##############################################
def __init__(self, prefix, classes):
self.prefix = prefix
self.classes = classes
number_of_positionals_min = 1000
number_of_positionals_max = 0
has_optionals = False
for element_class in classes:
number_of_positionals = element_class.number_of_positional_parameters
number_of_positionals_min = min(number_of_positionals_min, number_of_positionals)
number_of_positionals_max = max(number_of_positionals_max, number_of_positionals)
has_optionals = max(has_optionals, bool(element_class.optional_parameters))
self.number_of_positionals_min = number_of_positionals_min
self.number_of_positionals_max = number_of_positionals_max
self.has_optionals = has_optionals
self.multi_devices = len(classes) > 1
self.npins = prefix in ('Q', 'X') # NPinElement, Q has 3 to 4 pins
if self.npins:
self.number_of_pins = None
else:
# Q and X are single
self.number_of_pins = classes[0].number_of_pins
self.has_flag = False
for element_class in classes:
for parameter in element_class.optional_parameters.values():
if isinstance(parameter, FlagParameter):
self.has_flag = True
##############################################
def __len__(self):
return len(self.classes)
##############################################
def __iter__(self):
return iter(self.classes)
##############################################
@property
def single(self):
if not self.multi_devices:
return self.classes[0]
else:
raise NameError()
####################################################################################################
_prefix_cache = {prefix:PrefixData(prefix, classes)
for prefix, classes in ElementParameterMetaClass.__classes__.items()}
# for prefix_data in sorted(_prefix_cache.values(), key=lambda x: len(x)):
# print(prefix_data.prefix,
# len(prefix_data),
# prefix_data.number_of_positionals_min, prefix_data.number_of_positionals_max,
# prefix_data.has_optionals)
# Single:
# B 0 True
# D 1 True
# F 2 False
# G 1 False
# H 2 False
# I 1 False
# J 1 True
# K 3 False
# M 1 True
# S 2 False
# V 1 False
# W 3 False
# Z 1 True
# Two:
# E 0 1 False
# L 1 2 True
# Three:
# C 1 2 True
# R 1 2 True
# NPinElement:
# Q 1 1 True
# X 1 1 False
####################################################################################################
class Token:
""" This class implements a token, in fact a line in a Spice netlist. """
##############################################
def __init__(self, line):
self._line = line
##############################################
def __repr__(self):
return "{} {}".format(self.__class__.__name__, repr(self._line))
####################################################################################################
class Comment(Token):
pass
####################################################################################################
class Title(Token):
""" This class implements a title definition. """
##############################################
def __init__(self, line):
super().__init__(line)
self._title = self._line.read_right_of('.title')
##############################################
def __str__(self):
return self._title
##############################################
def __repr__(self):
return "Title {}".format(self._title)
####################################################################################################
class Include(Token):
""" This class implements a include definition. """
##############################################
def __init__(self, line):
super().__init__(line)
self._include = self._line.read_right_of('.include')
##############################################
def __str__(self):
return self._include
##############################################
def __repr__(self):
return "Include {}".format(self._title)
####################################################################################################
class Model(Token):
""" This class implements a model definition.
Spice syntax::
.model mname type (pname1=pval1 pname2=pval2)
"""
##############################################
def __init__(self, line):
super().__init__(line)
# Fixme
parameters, dict_parameters = self._line.split_line('.model')
self._name, self._model_type = parameters[:2]
self._parameters = dict_parameters
##############################################
@property
def name(self):
""" Name of the model """
return self._name
##############################################
def __repr__(self):
return "Model {} {} {}".format(self._name, self._model_type, self._parameters)
####################################################################################################
class SubCircuit(Token):
""" This class implements a sub-circuit definition.
Spice syntax::
.SUBCKT name node1 ... param1=value1 ...
"""
##############################################
def __init__(self, line):
super().__init__(line)
# Fixme:
parameters, dict_parameters = self._line.split_line('.subckt')
self._name, self._nodes = parameters[0], parameters[1:]
self._tokens = []
##############################################
@property
def name(self):
""" Name of the sub-circuit. """
return self._name
##############################################
def __repr__(self):
text = "SubCircuit {} {}\n".format(self._name, self._nodes)
text += '\n'.join([' ' + repr(token) for token in self._tokens])
return text
##############################################
def __iter__(self):
""" Return an iterator on the tokens. """
return iter(self._tokens)
##############################################
def append(self, token):
""" Append a token to the token's list. """
self._tokens .append(token)
####################################################################################################
class Element(Token):
""" This class implements an element definition.
"{ expression }" are allowed in device line.
"""
_logger = _module_logger.getChild('Element')
##############################################
def __init__(self, line):
super().__init__(line)
line_str = str(line)
# self._logger.debug('\n' + line_str)
# Retrieve device prefix
self._prefix = line_str[0]
prefix_data = _prefix_cache[self._prefix]
# Retrieve device name
start_location = 1
stop_location = line_str.find(' ')
# Fixme: if stop_location == -1:
self._name = line_str[start_location:stop_location]
self._nodes = []
self._parameters = []
self._dict_parameters = {}
# Read nodes
if not prefix_data.npins:
number_of_pins = prefix_data.number_of_pins
if number_of_pins:
self._nodes, stop_location = self._line.read_words(stop_location, number_of_pins)
else: # Q or X
if prefix_data.prefix == 'Q':
self._nodes, stop_location = self._line.read_words(stop_location, 3)
# Fixme: optional node
else: # X
args, stop_location = self._line.split_words(stop_location, until='=')
self._nodes = args[:-1]
self._parameters.append(args[-1]) # model name
# Read positionals
number_of_positionals = prefix_data.number_of_positionals_min
if number_of_positionals and stop_location is not None: # model is optional
self._parameters, stop_location = self._line.read_words(stop_location, number_of_positionals)
if prefix_data.multi_devices and stop_location is not None:
remaining, stop_location = self._line.split_words(stop_location, until='=')
self._parameters.extend(remaining)
if prefix_data.prefix in ('V', 'I') and stop_location is not None:
# merge remaining
self._parameters[-1] += line_str[stop_location:]
# Read optionals
if prefix_data.has_optionals and stop_location is not None:
kwargs, stop_location = self._line.split_words(stop_location)
for kwarg in kwargs:
try:
key, value = kwarg.split('=')
self._dict_parameters[key] = value
except ValueError:
if kwarg in ('off',) and prefix_data.has_flag:
self._dict_parameters['off'] = True
else:
self._logger.warn(line_str)
# raise NameError("Bad element line:", line_str)
if prefix_data.multi_devices:
for element_class in prefix_data:
if len(self._parameters) == element_class.number_of_positional_parameters:
break
else:
element_class = prefix_data.single
self.factory = element_class
# Move positionals passed as kwarg
to_delete = []
for parameter in element_class.positional_parameters.values():
if parameter.key_parameter:
i = parameter.position
self._dict_parameters[parameter.attribute_name] = self._parameters[i]
to_delete.append(i)
for i in to_delete:
del self._parameters[i]
self._logger.debug('\n' + self.__repr__())
##############################################
@property
def name(self):
""" Name of the element """
return self._name
##############################################
def __repr__(self):
return "Element {0._prefix} {0._name} {0._nodes} {0._parameters} {0._dict_parameters}".format(self)
####################################################################################################
class Line:
""" This class implements a line in the netlist. """
##############################################
def __init__(self, text, line_range):
text = str(text)
for marker in ('$', ';', '//'):
location = text.find(marker)
if location != -1:
break
if location != -1:
text = text[:location]
comment = text[location:]
else:
comment = ''
self._text = text
self._comment = comment
self._line_range = line_range
##############################################
def __repr__(self):
return "{0._line_range} {0._text}".format(self)
##############################################
def __str__(self):
return self._text
##############################################
def read_right_of(self, text):
return self._text[len(text):].strip()
##############################################
def read_words(self, start_location, number_of_words):
line_str = self._text
number_of_words_read = 0
words = []
while number_of_words_read < number_of_words: # and start_location < len(line_str)
stop_location = line_str.find(' ', start_location)
if stop_location == -1:
stop_location = None # read until end
word = line_str[start_location:stop_location].strip()
if word:
number_of_words_read += 1
words.append(word)
if stop_location is None: # we should stop
if number_of_words_read != number_of_words:
template = "Bad element line, looking for word {}/{}:\n"
raise NameError(template.format(number_of_words_read, number_of_words) +
line_str + '\n' +
' '*start_location + '^')
else:
if start_location < stop_location:
start_location = stop_location
else: # we have read a space
start_location += 1
return words, stop_location
##############################################
def split_words(self, start_location, until=None):
line_str = self._text
stop_location = None
if until is not None:
location = line_str.find(until, start_location)
if location != -1:
stop_location = location
location = line_str.rfind(' ', start_location, stop_location)
if location != -1:
stop_location = location
else:
raise NameError("Bad element line, missing key? " + line_str)
line_str = line_str[start_location:stop_location]
words = [x for x in line_str.split(' ') if x]
return words, stop_location
##############################################
def split_line(self, keyword):
""" Split the line according to the following pattern::
keyword parameter1 parameter2 ... key1=value1 key2=value2 ...
Return the list of parameters and the dictionnary.
"""
raw_parameters = self._text[len(keyword):].split()
parameters = []
dict_parameters = {}
for parameter in raw_parameters:
if '=' in parameter:
key, value = parameter.split('=')
dict_parameters[key.strip()] = value.strip()
else:
parameters.append(parameter)
return parameters, dict_parameters
####################################################################################################
class SpiceParser:
""" This class parse a Spice netlist file and build a syntax tree.
Public Attributes:
:attr:`circuit`
:attr:`models`
:attr:`subcircuits`
"""
_logger = _module_logger.getChild('SpiceParser')
##############################################
def __init__(self, path=None, source=None):
# Fixme: empty source
if path is not None:
with open(str(path), 'r') as f:
raw_lines = f.readlines()
elif source is not None:
raw_lines = source.split('\n') # Fixme: other os
else:
raise ValueError
lines = self._merge_lines(raw_lines)
self._title = None
self._tokens = self._parse(lines)
self._find_sections()
##############################################
def _merge_lines(self, raw_lines):
"""Merge broken lines and return a new list of lines.
A line starting with "+" continues the preceding line.
"""
# Fixme: better using lines[-1] ?
lines = []
current_line = ''
current_line_index = None
for line_index, line in enumerate(raw_lines):
if line.startswith('+'):
current_line += ' ' + line[1:].strip()
else:
if current_line:
lines.append(Line(current_line, slice(current_line_index, line_index)))
current_line = line.strip()
current_line_index = line_index
if current_line:
lines.append(Line(current_line, slice(current_line_index, len(raw_lines))))
return lines
##############################################
def _parse(self, lines):
""" Parse the lines and return a list of tokens. """
tokens = []
sub_circuit = None
scope = tokens
for line in lines:
# print repr(line)
text = str(line)
lower_case_text = text.lower() # !
if text.startswith('*'):
scope.append(Comment(line))
elif lower_case_text.startswith('.'):
lower_case_text = lower_case_text[1:]
if lower_case_text.startswith('subckt'):
sub_circuit = SubCircuit(line)
tokens.append(sub_circuit)
scope = sub_circuit
elif lower_case_text.startswith('ends'):
sub_circuit = None
scope = tokens
elif lower_case_text.startswith('title'):
self._title = Title(line)
scope.append(self._title)
elif lower_case_text.startswith('end'):
pass
elif lower_case_text.startswith('model'):
model = Model(line)
scope.append(model)
elif lower_case_text.startswith('include'):
scope.append(Include(line))
else:
# options param ...
# .global
# .lib filename libname
# .param
# .func .csparam .temp .if
# { expr } are allowed in .model lines and in device lines.
self._logger.warn(line)
else:
element = Element(line)
scope.append(element)
return tokens
##############################################
def _find_sections(self):
""" Look for model, sub-circuit and circuit definitions in the token list. """
self.circuit = None
self.subcircuits = []
self.models = []
for token in self._tokens:
if isinstance(token, Title):
if self.circuit is None:
self.circuit = token
else:
raise NameError("More than one title")
elif isinstance(token, SubCircuit):
self.subcircuits.append(token)
elif isinstance(token, Model):
self.models.append(token)
##############################################
def is_only_subcircuit(self):
return bool(not self.circuit and self.subcircuits)
##############################################
def is_only_model(self):
return bool(not self.circuit and not self.subcircuits and self.models)
##############################################
def build_circuit(self, ground=0):
ground = str(ground)
circuit = Circuit(str(self._title))
for token in self._tokens:
if isinstance(token, Include):
circuit.include(str(token))
for token in self._tokens:
if isinstance(token, Element):
factory = getattr(circuit, token.factory.alias)
nodes = []
for node in token._nodes:
if str(node) == ground:
node = 0
nodes.append(node)
if token._prefix != 'X':
args = nodes + token._parameters
else: # != Spice
args = token._parameters + nodes
kwargs = token._dict_parameters
message = ' '.join([str(x) for x in (token._prefix, token._name, nodes,
token._parameters, token._dict_parameters)])
self._logger.debug(message)
factory(token._name, *args, **kwargs)
return circuit
##############################################
def _to_python(self, value):
try:
int_value = int(value)
value = float(value)
if int_value == value:
return str(int_value)
else:
return str(value)
except ValueError:
return "'{}'".format(value)
##############################################
def to_python_code(self, ground=0):
ground = str(ground)
# for token in self._tokens:
# if isinstance(token, Include):
# circuit.include(str(token))
if self._title:
title = self._title
else:
title = '...'
circuit = "circuit = Circuit('{}')\n".format(title)
for token in self._tokens:
if isinstance(token, Element):
nodes = []
for node in token._nodes:
if str(node) == ground:
node = 0
nodes.append(node)
if token._prefix != 'X':
args = nodes + token._parameters
else: # != Spice
args = token._parameters + nodes
args = [self._to_python(x) for x in args]
kwargs = ['{}={}'.format(key, self._to_python(value))
for key, value in token._dict_parameters.items()]
parameters = ', '.join(args + kwargs)
circuit += "circuit.{}({})\n".format(token._prefix, parameters)
return circuit
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/RawFile.py 0000664 0000000 0000000 00000033726 13053355342 0017616 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""
Header
.. code::
Circuit: 230V Rectifier
Doing analysis at TEMP = 25.000000 and TNOM = 25.000000
Title: 230V Rectifier
Date: Thu Jun 4 23:40:58 2015
Plotname: Transient Analysis
Flags: real
No. Variables: 6
No. Points: 0
Variables:
No. of Data Columns : 6
0 time time
1 v(in) voltage
...
5 i(vinput) current
Binary:
Operating Point
Node voltages and source branch currents:
* v(node_name)
* i(vname)
Sensitivity Analysis
* v({element})
* v({element}_{parameter})
* v(v{source})
DC
* v(v-sweep)
* v({node})
* i(v{source})
AC
Frequency, node voltages and source branch currents:
* frequency
* v({node})
* i(v{name})
Transient Analysis
Time, node voltages and source branch currents:
* time
* v({node})
* i(v{source})
"""
# * v({element}:bv_max)
# * i(e.xdz1.ev1)
####################################################################################################
import logging
import numpy as np
####################################################################################################
from ..Probe.WaveForm import (OperatingPoint, SensitivityAnalysis,
DcAnalysis, AcAnalysis, TransientAnalysis,
WaveForm)
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class Variable:
"""This class implements a variable or probe in a SPICE simulation output.
Public Attributes:
:attr:`index`
index in the array
:attr:`name`
:attr:`unit`
"""
##############################################
def __init__(self, index, name, unit):
self.index = int(index)
self.name = str(name)
self.unit = str(unit) # could be guessed from name also for voltage node and branch current
self.data = None
##############################################
def __repr__(self):
return 'variable[{self.index}]: {self.name} [{self.unit}]'.format(self=self)
##############################################
def is_voltage_node(self):
return self.name.startswith('v(')
##############################################
def is_branch_current(self):
# source branch current
return self.name.startswith('i(')
##############################################
@staticmethod
def to_voltage_name(node):
return 'v({})'.format(node)
##############################################
@staticmethod
def to_branch_name(element):
return 'i({})'.format(element)
##############################################
@property
def simplified_name(self):
if self.is_voltage_node() or self.is_branch_current():
return self.name[2:-1]
else:
return self.name
##############################################
def fix_case(self, element_translation, node_translation):
""" Update the name to the right case. """
if self.is_branch_current():
if self.simplified_name in element_translation:
self.name = self.to_branch_name(element_translation[self.simplified_name])
elif self.is_voltage_node():
if self.simplified_name in node_translation:
self.name = self.to_voltage_name(node_translation[self.simplified_name])
##############################################
def to_waveform(self, abscissa=None, to_real=False, to_float=False):
""" Return a :obj:`PySpice.Probe.WaveForm` instance. """
data = self.data
if to_real:
data = data.real
if to_float:
data = float(data[0])
return WaveForm(self.simplified_name, self.unit, data, abscissa=abscissa)
####################################################################################################
class RawFile:
""" This class parse the stdout of ngspice and the raw data output.
Public Attributes:
:attr:`circuit`
same as title
:attr:`data`
:attr:`date`
:attr:`flags`
'real' or 'complex'
:attr:`number_of_points`
:attr:`number_of_variables`
:attr:`plot_name`
AC Analysis, Operating Point, Sensitivity Analysis, DC transfer characteristic
:attr:`temperature`
:attr:`title`
:attr:`variables`
:attr:`warnings`
"""
_logger = _module_logger.getChild('RawFile')
##############################################
def __init__(self, stdout, number_of_points):
self.number_of_points = number_of_points
raw_data = self._read_header(stdout)
self._read_variable_data(raw_data)
# self._to_analysis()
##############################################
def _read_header(self, stdout):
""" Parse the header """
binary_line = b'Binary:\n'
binary_location = stdout.find(binary_line)
if binary_location < 0:
raise NameError('Cannot locate binary data')
raw_data_start = binary_location + len(binary_line)
# self._logger.debug('\n' + stdout[:raw_data_start].decode('utf-8'))
header_lines = stdout[:binary_location].splitlines()
raw_data = stdout[raw_data_start:]
header_line_iterator = iter(header_lines)
self.circuit = self._read_header_field_line(header_line_iterator, 'Circuit')
self.temperature = self._read_header_line(header_line_iterator, 'Doing analysis at TEMP')
self.warnings = [self._read_header_field_line(header_line_iterator, 'Warning')
for i in range(stdout.count(b'Warning'))]
for warning in self.warnings:
self._logger.warn(warning)
self.title = self._read_header_field_line(header_line_iterator, 'Title')
self.date = self._read_header_field_line(header_line_iterator, 'Date')
self.plot_name = self._read_header_field_line(header_line_iterator, 'Plotname')
self.flags = self._read_header_field_line(header_line_iterator, 'Flags')
self.number_of_variables = int(self._read_header_field_line(header_line_iterator, 'No. Variables'))
self._read_header_field_line(header_line_iterator, 'No. Points')
self._read_header_field_line(header_line_iterator, 'Variables', has_value=False)
self._read_header_field_line(header_line_iterator, 'No. of Data Columns ')
self.variables = {}
for i in range(self.number_of_variables):
line = (next(header_line_iterator)).decode('utf-8')
self._logger.debug(line)
items = [x.strip() for x in line.split('\t') if x]
# 0 frequency frequency grid=3
index, name, unit = items[:3]
self.variables[name] = Variable(index, name, unit)
# self._read_header_field_line(header_line_iterator, 'Binary', has_value=False)
return raw_data
##############################################
def _read_line(self, header_line_iterator):
""" Return the next line """
# Fixme: self._header_line_iterator, etc.
line = None
while not line:
line = next(header_line_iterator)
return line.decode('utf-8')
##############################################
def _read_header_line(self, header_line_iterator, head_line):
""" Read an header line and check it starts with *head_line*. """
line = self._read_line(header_line_iterator)
self._logger.debug(line)
if not line.startswith(head_line):
raise NameError("Unexpected line: %s" % (line))
##############################################
def _read_header_field_line(self, header_line_iterator, expected_label, has_value=True):
""" Read an header line and check it starts with *expected_label*.
Return the values next to the label if the flag *has_value* is set.
"""
line = self._read_line(header_line_iterator)
self._logger.debug(line)
if has_value:
# a title can have ': ' after 'title: '
location = line.find(': ') # first occurence
label, value = line[:location], line[location+2:]
else:
label = line[:-1]
if label != expected_label:
raise NameError("Expected label %s instead of %s" % (expected_label, label))
if has_value:
return value.strip()
##############################################
def _read_variable_data(self, raw_data):
""" Read the raw data and set the variable values. """
if self.flags == 'real':
number_of_columns = self.number_of_variables
elif self.flags == 'complex':
number_of_columns = 2*self.number_of_variables
else:
raise NotImplementedError
input_data = np.fromstring(raw_data, count=number_of_columns*self.number_of_points, dtype='f8')
input_data = input_data.reshape((self.number_of_points, number_of_columns))
input_data = input_data.transpose()
# np.savetxt('raw.txt', input_data)
if self.flags == 'complex':
raw_data = input_data
input_data = np.array(raw_data[0::2], dtype='complex64')
input_data.imag = raw_data[1::2]
for variable in self.variables.values():
variable.data = input_data[variable.index]
##############################################
def fix_case(self, circuit):
""" Ngspice return lower case names. This method fixes the case of the variable names. """
element_translation = {element.lower():element for element in circuit.element_names()}
node_translation = {node.lower():node for node in circuit.node_names()}
for variable in self.variables.values():
variable.fix_case(element_translation, node_translation)
##############################################
def nodes(self, to_float=False, abscissa=None):
return [variable.to_waveform(abscissa, to_float=to_float)
for variable in self.variables.values()
if variable.is_voltage_node()]
##############################################
def branches(self, to_float=False, abscissa=None):
return [variable.to_waveform(abscissa, to_float=to_float)
for variable in self.variables.values()
if variable.is_branch_current()]
##############################################
def elements(self, abscissa=None):
return [variable.to_waveform(abscissa, to_float=True)
for variable in self.variables.values()]
##############################################
def to_analysis(self, circuit):
self.fix_case(circuit)
if self.plot_name == 'Operating Point':
return self._to_operating_point_analysis()
elif self.plot_name == 'Sensitivity Analysis':
return self._to_sensitivity_analysis()
elif self.plot_name == 'DC transfer characteristic':
return self._to_dc_analysis()
elif self.plot_name == 'AC Analysis':
return self._to_ac_analysis()
elif self.plot_name == 'Transient Analysis':
return self._to_transient_analysis()
else:
raise NotImplementedError("Unsupported plot name {}".format(self.plot_name))
##############################################
def _to_operating_point_analysis(self):
return OperatingPoint(nodes=self.nodes(to_float=True), branches=self.branches(to_float=True))
##############################################
def _to_sensitivity_analysis(self):
# Fixme: test .SENS I (VTEST)
# Fixme: separate v(vinput), analysis.R2.m
return SensitivityAnalysis(elements=self.elements())
##############################################
def _to_dc_analysis(self):
if 'v(v-sweep)' in self.variables:
sweep_variable = self.variables['v(v-sweep)']
elif 'v(i-sweep)' in self.variables:
sweep_variable = self.variables['v(i-sweep)']
else:
#
raise NotImplementedError
sweep = sweep_variable.to_waveform()
return DcAnalysis(sweep, nodes=self.nodes(), branches=self.branches())
##############################################
def _to_ac_analysis(self):
frequency = self.variables['frequency'].to_waveform(to_real=True)
return AcAnalysis(frequency, nodes=self.nodes(), branches=self.branches())
##############################################
def _to_transient_analysis(self):
time = self.variables['time'].to_waveform(to_real=True)
return TransientAnalysis(time, nodes=self.nodes(abscissa=time), branches=self.branches(abscissa=time))
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/Server.py 0000664 0000000 0000000 00000013465 13053355342 0017531 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
"""This module provides an interface to run ngspice in server mode and get back the simulation
output.
When ngspice runs in server mode, it writes on the standard output an header and then the simulation
output in binary format. At the end of the simulation, it writes on the standard error a line of
the form:
.. code::
@@@ \d+ \d+
where the second number is the number of points of the simulation. Due to the iterative and
adaptive nature of a transient simulation, the number of points is only known at the end.
Any line starting with "Error" in the standard output indicates an error in the simulation process.
The line "run simulation(s) aborted" in the standard error indicates the simulation aborted.
Any line starting with *Warning* in the standard error indicates non critical error in the
simulation process.
"""
####################################################################################################
import logging
import re
import subprocess
####################################################################################################
from .RawFile import RawFile
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class SpiceServer:
"""This class wraps the execution of ngspice in server mode and convert the output to a Python data
structure.
Example of usage::
spice_server = SpiceServer(spice_command='/path/to/ngspice')
raw_file = spice_server(spice_input)
It returns a :obj:`PySpice.Spice.RawFile` instance.
"""
_logger = _module_logger.getChild('SpiceServer')
##############################################
def __init__(self, spice_command='ngspice'):
self._spice_command = spice_command
##############################################
def _decode_number_of_points(self, line):
"""Decode the number of points in the given line."""
match = re.match(r'@@@ (\d+) (\d+)', line)
if match is not None:
return int(match.group(2))
else:
raise NameError("Cannot decode the number of points")
##############################################
def _parse_stdout(self, stdout):
"""Parse stdout for errors."""
# self._logger.debug('\n' + stdout)
error_found = False
# UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 870: invalid start byte
# lines = stdout.decode('utf-8').splitlines()
lines = stdout.splitlines()
for line_index, line in enumerate(lines):
if line.startswith(b'Error '):
error_found = True
self._logger.error('\n' + line.decode('utf-8') + '\n' + lines[line_index+1].decode('utf-8'))
if error_found:
raise NameError("Errors was found by Spice")
##############################################
def _parse_stderr(self, stderr):
"""Parse stderr for warnings and return the number of points."""
self._logger.debug('\n' + stderr)
stderr_lines = stderr.splitlines()
number_of_points = None
for line in stderr_lines:
if line.startswith('Warning:'):
self._logger.warning(line[len('Warning :'):])
elif line == 'run simulation(s) aborted':
raise NameError("Simulation aborted\n" + stderr)
elif line.startswith('@@@'):
number_of_points = self._decode_number_of_points(line)
return number_of_points
##############################################
def __call__(self, spice_input):
"""Run SPICE in server mode as a subprocess for the given input and return a
:obj:`PySpice.RawFile.RawFile` instance.
"""
self._logger.info("Start the spice subprocess")
process = subprocess.Popen((self._spice_command, '-s'),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
input_ = str(spice_input).encode('utf-8')
stdout, stderr = process.communicate(input_)
# stdout = stdout.decode('utf-8')
stderr = stderr.decode('utf-8')
self._parse_stdout(stdout)
number_of_points = self._parse_stderr(stderr)
if number_of_points is None:
raise NameError("The number of points was not found in the standard error buffer,"
" ngspice returned:\n" +
stderr)
return RawFile(stdout, number_of_points)
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/Simulation.py 0000664 0000000 0000000 00000034721 13053355342 0020405 0 ustar 00root root 0000000 0000000 ###################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import logging
####################################################################################################
from ..Tools.StringTools import join_list, join_dict
from .NgSpice.Shared import NgSpiceShared
from .Server import SpiceServer
####################################################################################################
_module_logger = logging.getLogger(__name__)
####################################################################################################
class CircuitSimulation:
"""Define and generate the spice instruction to perform a circuit simulation.
.. warning:: In some cases NgSpice can perform several analyses one after the other. This case
is partially supported.
"""
_logger = _module_logger.getChild('CircuitSimulation')
##############################################
def __init__(self, circuit,
temperature=27,
nominal_temperature=27,
pipe=True,
):
self._circuit = circuit
self._options = {} # .options
self._initial_condition = {} # .ic
self._saved_nodes = ()
self._analysis_parameters = {}
self.temperature = temperature
self.nominal_temperature = nominal_temperature
if pipe:
self.options('NOINIT')
self.options(filetype='binary')
##############################################
@property
def circuit(self):
return self._circuit
##############################################
def options(self, *args, **kwargs):
for item in args:
self._options[str(item)] = None
for key, value in kwargs.items():
self._options[str(key)] = str(value)
##############################################
@property
def temperature(self):
return self._options['TEMP']
@temperature.setter
def temperature(self, value):
self._options['TEMP'] = value
##############################################
@property
def nominal_temperature(self):
return self._options['TNOM']
@nominal_temperature.setter
def nominal_temperature(self, value):
self._options['TNOM'] = value
##############################################
def initial_condition(self, **kwargs):
""" Set initial condition for voltage nodes.
Usage: initial_condition(node_name1=value, ...)
"""
for key, value in kwargs.items():
self._initial_condition['V({})'.format(str(key))] = str(value)
# Fixme: .nodeset
##############################################
def save(self, *args):
# Fixme: pass Node for voltage node, Element for source branch current, ...
"""Set the list of saved vectors.
If no *.save* line is given, then the default set of vectors is saved (node voltages and
voltage source branch currents). If *.save* lines are given, only those vectors specified
are saved.
Node voltages may be saved by giving the node_name or *v(node_name)*. Currents through an
independent voltage source (including inductor) are given by *i(source_name)* or
*source_name#branch*. Internal device data are accepted as *@dev[param]*.
If you want to save internal data in addition to the default vector set, add the parameter
*all* to the additional vectors to be saved.
"""
self._saved_nodes = list(args)
##############################################
@property
def save_currents(self):
""" Save all currents. """
return self._options.get('SAVECURRENTS', False)
@save_currents.setter
def save_currents(self, value):
if value:
self._options['SAVECURRENTS'] = True
else:
del self._options['SAVECURRENTS']
##############################################
def reset_analysis(self):
self._analysis_parameters.clear()
##############################################
def operating_point(self):
"""Compute the operating point of the circuit with capacitors open and inductors shorted."""
self._analysis_parameters['op'] = ''
##############################################
def dc_sensitivity(self, output_variable):
"""Compute the sensitivity of the DC operating point of a node voltage or voltage-source branch
current to all non-zero device parameters.
General form:
.. code::
.sens outvar
Examples:
.. code::
.SENS V(1, OUT)
.SENS I(VTEST)
"""
self._analysis_parameters['sens'] = (output_variable,)
##############################################
def ac_sensitivity(self, output_variable,
start_frequency, stop_frequency, number_of_points, variation):
"""Compute the sensitivity of the AC values of a node voltage or voltage-source branch
current to all non-zero device parameters.
General form:
.. code::
.sens outvar ac dec nd fstart fstop
.sens outvar ac oct no fstart fstop
.sens outvar ac lin np fstart fstop
Examples:
.. code::
.SENS V(OUT) AC DEC 10 100 100 k
"""
if variation not in ('dec', 'oct', 'lin'):
raise ValueError("Incorrect variation type")
self._analysis_parameters['sens'] = (output_variable,
variation, number_of_points, start_frequency, stop_frequency)
##############################################
def dc(self, **kwargs):
"""Compute the DC transfer fonction of the circuit with capacitors open and inductors shorted.
General form:
.. code::
.dc srcnam vstart vstop vincr [ src2 start2 stop2 incr2 ]
*srcnam* is the name of an independent voltage or current source, a resistor or the circuit
temperature. *vstart*, *vstop*, and *vincr* are the starting, final, and incrementing values
respectively.
A second source (*src2*) may optionally be specified with associated sweep parameters. In
this case, the first source is swept over its range for each value of the second source.
Examples:
.. code::
.dc VIN 0 .2 5 5.0 0.25
.dc VDS 0 10 .5 VGS 0 5 1
.dc VCE 0 10 .2 5 IB 0 10U 1U
.dc RLoad 1k 2k 100
.dc TEMP -15 75 5
"""
parameters = []
for variable, value_slice in kwargs.items():
variable_lower = variable.lower()
if variable_lower[0] in ('v', 'i', 'r') or variable_lower == 'temp':
parameters += [variable, value_slice.start, value_slice.stop, value_slice.step]
else:
raise NameError('Sweep variable must be a voltage/current source, '
'a resistor or the circuit temperature')
self._analysis_parameters['dc'] = parameters
##############################################
def ac(self, start_frequency, stop_frequency, number_of_points, variation):
# fixme: concise keyword ?
"""Perform a small-signal AC analysis of the circuit where all non-linear devices are linearized
around their actual DC operating point.
Note that in order for this analysis to be meaningful, at least one independent source must
have been specified with an AC value. Typically it does not make much sense to specify more
than one AC source. If you do, the result will be a superposition of all sources, thus
difficult to interpret.
Examples:
.. code::
.ac dec nd fstart fstop
.ac oct no fstart fstop
.ac lin np fstart fstop
The parameter *variation* must be either `dec`, `oct` or `lin`.
"""
if variation not in ('dec', 'oct', 'lin'):
raise ValueError("Incorrect variation type")
self._analysis_parameters['ac'] = (variation, number_of_points, start_frequency, stop_frequency)
##############################################
def transient(self, step_time, end_time, start_time=None, max_time=None,
use_initial_condition=False):
"""Perform a transient analysis of the circuit.
General Form:
.. code::
.tran tstep tstop >
"""
if use_initial_condition:
uic = 'uic'
else:
uic = None
self._analysis_parameters['tran'] = (step_time, end_time, start_time, max_time, uic)
##############################################
def __str__(self):
netlist = str(self._circuit)
if self.options:
for key, value in self._options.items():
if value is not None:
netlist += '.options {} = {}\n'.format(key, value)
else:
netlist += '.options {}\n'.format(key)
if self.initial_condition:
netlist += '.ic ' + join_dict(self._initial_condition) + '\n'
if self._saved_nodes:
netlist += '.save ' + join_list(self._saved_nodes) + '\n'
for analysis, analysis_parameters in self._analysis_parameters.items():
netlist += '.' + analysis + ' ' + join_list(analysis_parameters) + '\n'
netlist += '.end\n'
return netlist
####################################################################################################
class CircuitSimulator(CircuitSimulation):
""" This class implements a circuit simulator. Each analysis mode is performed by a method that
return the measured probes.
For *ac* and *transient* analyses, the user must specify a list of nodes using the *probes* key
argument.
"""
_logger = _module_logger.getChild('CircuitSimulator')
##############################################
def _run(self, analysis_method, *args, **kwargs):
self.reset_analysis()
if 'probes' in kwargs:
self.save(* kwargs.pop('probes'))
method = getattr(CircuitSimulation, analysis_method)
method(self, *args, **kwargs)
self._logger.debug('desk\n' + str(self))
##############################################
def operating_point(self, *args, **kwargs):
return self._run('operating_point', *args, **kwargs)
##############################################
def dc(self, *args, **kwargs):
return self._run('dc', *args, **kwargs)
##############################################
def dc_sensitivity(self, *args, **kwargs):
return self._run('dc_sensitivity', *args, **kwargs)
##############################################
def ac(self, *args, **kwargs):
return self._run('ac', *args, **kwargs)
##############################################
def transient(self, *args, **kwargs):
return self._run('transient', *args, **kwargs)
####################################################################################################
class SubprocessCircuitSimulator(CircuitSimulator):
_logger = _module_logger.getChild('SubprocessCircuitSimulator')
##############################################
def __init__(self, circuit,
temperature=27,
nominal_temperature=27,
spice_command='ngspice',
):
# Fixme: kwargs
super().__init__(circuit, temperature, nominal_temperature, pipe=True)
self._spice_server = SpiceServer()
##############################################
def _run(self, analysis_method, *args, **kwargs):
super()._run(analysis_method, *args, **kwargs)
raw_file = self._spice_server(str(self))
self.reset_analysis()
# for field in raw_file.variables:
# print field
return raw_file.to_analysis(self._circuit)
####################################################################################################
class NgSpiceSharedCircuitSimulator(CircuitSimulator):
_logger = _module_logger.getChild('NgSpiceSharedCircuitSimulator')
##############################################
def __init__(self, circuit,
temperature=27,
nominal_temperature=27,
ngspice_shared=None,
):
# Fixme: kwargs
super().__init__(circuit, temperature, nominal_temperature, pipe=False)
if ngspice_shared is None:
self._ngspice_shared = NgSpiceShared(send_data=False)
else:
self._ngspice_shared = ngspice_shared
##############################################
def _run(self, analysis_method, *args, **kwargs):
super()._run(analysis_method, *args, **kwargs)
self._ngspice_shared.load_circuit(str(self))
self._ngspice_shared.run()
self._logger.debug(str(self._ngspice_shared.plot_names))
self.reset_analysis()
if analysis_method == 'dc':
plot_name = 'dc1'
elif analysis_method == 'ac':
plot_name = 'ac1'
elif analysis_method == 'transient':
plot_name = 'tran1'
else:
raise NotImplementedError
return self._ngspice_shared.plot(plot_name).to_analysis()
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Spice/__init__.py 0000664 0000000 0000000 00000004724 13053355342 0020020 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
from . import BasicElement
from . import HighLevelElement
from .Netlist import Netlist, ElementParameterMetaClass
####################################################################################################
def _get_elements(module):
element_classes = []
for item in module.__dict__.values():
if (type(item) is ElementParameterMetaClass
and item.prefix is not None
):
element_classes.append(item)
return element_classes
####################################################################################################
#
# Add a method to create elements to the Netlist class
#
spice_elements = _get_elements(BasicElement)
high_level_elements = _get_elements(HighLevelElement)
for element_class in spice_elements + high_level_elements:
def _make_function(element_class):
def function(self, *args, **kwargs):
element = element_class(*args, **kwargs)
self._add_element(element)
return element
return function
if element_class in spice_elements and hasattr(element_class, 'alias'):
function_name = element_class.alias
else:
function_name = element_class.__name__
setattr(Netlist, function_name, _make_function(element_class))
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Tools/ 0000775 0000000 0000000 00000000000 13053355342 0015735 5 ustar 00root root 0000000 0000000 PySpice-v0.3.2/PySpice/Tools/EnumFactory.py 0000664 0000000 0000000 00000012126 13053355342 0020545 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
""" This module provides an implementation for enumerate.
The enumerate factory :func:`EnumFactory` builds a enumerate from a list of names and assigns to
these constants a value from 0 to N-1, where N is the number of constants. For example::
enum = EnumFactory('Enum1', ('cst1', 'cst2'))
builds a enumerate with *cst1* set to 0 and *cst2* set to 1.
We can get a constant's value using an integer context like::
int(enum.cst1)
and the constant's name using::
repr(enum.cst1)
We can test constant equality using::
enum1.cst == enum2.cst
or with something that understand the *int* protocol::
enum1.cst == obj
# equivalent to
int(enum1.cst) == int(obj)
The number of constants could be retrieved with::
len(enum)
The enumerate factory :func:`ExplicitEnumFactory` is a variant that permits to specify the values of
the constants::
enum2 = ExplicitEnumFactory('Enum2', {'cst1':1, 'cst2':3})
We can test if a value is in the enumerate using::
constant_value in enum2
"""
####################################################################################################
# __all__ = ['EnumFactory', 'ExplicitEnumFactory']
####################################################################################################
class ReadOnlyMetaClass(type):
""" This meta class implements a class where attributes are read only. """
##############################################
def __setattr__(self, name, value):
raise NotImplementedError
####################################################################################################
class EnumMetaClass(ReadOnlyMetaClass):
""" This meta class implements the :func:`len` protocol. """
##############################################
def __len__(self):
return self._size
##############################################
def __getitem__(self, i):
return self._index[i]
####################################################################################################
class ExplicitEnumMetaClass(ReadOnlyMetaClass):
""" This meta class implements the operator ``in``. """
##############################################
def __contains__(self, item):
return item in self.constants
####################################################################################################
class EnumConstant:
""" Define an Enum Constant """
##############################################
def __init__(self, name, value):
self._name = name
self._value = value
##############################################
def __eq__(self, other):
return self._value == int(other)
##############################################
def __int__(self):
return self._value
##############################################
def __repr__(self):
return self._name
####################################################################################################
def EnumFactory(enum_name, enum_tuple):
""" Return an :class:`EnumMetaClass` instance, where *enum_name* is the class name and
*enum_tuple* is an iterable of constant's names.
"""
index = [EnumConstant(name, value) for value, name in enumerate(enum_tuple)]
obj_dict = {}
obj_dict['_size'] = len(enum_tuple)
obj_dict['_index'] = index
obj_dict.update({str(enum):enum for enum in index})
return EnumMetaClass(enum_name, (), obj_dict)
####################################################################################################
def ExplicitEnumFactory(enum_name, enum_dict):
""" Return an :class:`ExplicitEnumMetaClass` instance, where *enum_name* is the class name and
*enum_dict* is a dict of constant's names and their values.
"""
obj_dict = {}
obj_dict['constants'] = list(enum_dict.values())
for name, value in list(enum_dict.items()):
obj_dict[name] = EnumConstant(name, value)
return ExplicitEnumMetaClass(enum_name, (), obj_dict)
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Tools/File.py 0000664 0000000 0000000 00000017204 13053355342 0017172 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import os
import subprocess
####################################################################################################
def file_name_has_extension(file_name, extension):
return file_name.endswith(extension)
####################################################################################################
def file_extension(filename):
# index = filename.rfind(os.path.extsep)
# if index == -1:
# return None
# else:
# return filename[index:]
return os.path.splitext(filename)[1]
####################################################################################################
def run_shasum(filename, algorithm=1, text=False, binary=False, portable=False):
if algorithm not in (1, 224, 256, 384, 512, 512224, 512256):
raise ValueError
args = ['shasum', '--algorithm=' + str(algorithm)]
if text:
args.append('--text')
elif binary:
args.append('--binary')
elif portable:
args.append('--portable')
args.append(filename)
output = subprocess.check_output(args)
shasum = output[:output.find(' ')]
return shasum
####################################################################################################
class Path:
##############################################
def __init__(self, path):
self._path = str(path)
##############################################
def __bool__(self):
return os.path.exists(self._path)
##############################################
def __str__(self):
return self._path
##############################################
@property
def path(self):
return self._path
##############################################
def is_absolut(self):
return os.path.isabs(self._path)
##############################################
def absolut(self):
return self.clone_for_path(os.path.abspath(self._path))
##############################################
def normalise(self):
return self.clone_for_path(os.path.normpath(self._path))
##############################################
def normalise_case(self):
return self.clone_for_path(os.path.normcase(self._path))
##############################################
def expand_vars_and_user(self):
return self.clone_for_path(os.path.expandvars(os.path.expanduser(self._path)))
##############################################
def real_path(self):
return self.clone_for_path(os.path.realpath(self._path))
##############################################
def relative_to(self, directory):
return self.clone_for_path(os.path.relpath(self._path, str(directory)))
##############################################
def clone_for_path(self, path):
return self.__class__(path)
##############################################
def split(self):
return self._path.split(os.path.sep)
##############################################
def directory_part(self):
return Directory(os.path.dirname(self._path))
##############################################
def filename_part(self):
return os.path.basename(self._path)
##############################################
def is_directory(self):
return os.path.isdir(self._path)
##############################################
def is_file(self):
return os.path.isfile(self._path)
##############################################
@property
def inode(self):
return os.stat(self._path).st_ino
##############################################
@property
def creation_time(self):
return os.stat(self._path).st_ctime
####################################################################################################
class Directory(Path):
##############################################
def __bool__(self):
return super().__nonzero__() and self.is_directory()
##############################################
def join_directory(self, directory):
return self.__class__(os.path.join(self._path, str(directory)))
##############################################
def join_filename(self, filename):
return File(filename, self._path)
##############################################
def iter_file(self, followlinks=False):
for root, directories, files in os.walk(self._path, followlinks=followlinks):
for filename in files:
yield File(filename, root)
##############################################
def iter_directories(self, followlinks=False):
for root, directories, files in os.walk(self._path, followlinks=followlinks):
for directory in directories:
yield Path(os.path.join(root, directory))
####################################################################################################
class File(Path):
default_shasum_algorithm = 256
##############################################
def __init__(self, filename, path=''):
super().__init__(os.path.join(str(path), str(filename)))
self._filename = self.filename_part()
if not self._filename:
raise ValueError
self._directory = self.directory_part()
self._shasum = None # lazy computation
##############################################
def __bool__(self):
return super().__nonzero__() and os.path.isfile(self._path)
##############################################
@property
def directory(self):
return self._directory
##############################################
@property
def filename(self):
return self._filename
##############################################
@property
def extension(self):
return file_extension(self._filename)
##############################################
@property
def shasum(self):
if self._shasum is None:
return self.compute_shasum()
else:
return self._shasum
##############################################
def compute_shasum(self, algorithm=None):
if algorithm is None:
algorithm = self.default_shasum_algorithm
self._shasum = run_shasum(self._path, algorithm, portable=True)
return self._shasum
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Tools/Path.py 0000664 0000000 0000000 00000004367 13053355342 0017215 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import os
import types
####################################################################################################
def to_absolute_path(path):
# Expand ~ . and Remove trailing '/'
return os.path.abspath(os.path.expanduser(path))
####################################################################################################
def parent_directory_of(file_name, step=1):
directory = file_name
for i in range(step):
directory = os.path.dirname(directory)
return directory
####################################################################################################
def find(file_name, directories):
if isinstance(directories, bytes):
directories = (directories,)
for directory in directories:
for directory_path, sub_directories, file_names in os.walk(directory):
if file_name in file_names:
return os.path.join(directory_path, file_name)
raise NameError("File %s not found in directories %s" % (file_name, str(directories)))
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Tools/StringTools.py 0000664 0000000 0000000 00000003350 13053355342 0020577 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
def join_lines(items, prefix=''):
return '\n'.join([prefix + str(item) for item in items if item is not None])
####################################################################################################
def join_list(items):
return ' '.join([str(item) for item in items if item is not None])
####################################################################################################
def join_dict(d):
return ' '.join(["{}={}".format(key, value) for key, value in d.items() if value is not None])
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Tools/__init__.py 0000664 0000000 0000000 00000001630 13053355342 0020046 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
PySpice-v0.3.2/PySpice/Unit/ 0000775 0000000 0000000 00000000000 13053355342 0015554 5 ustar 00root root 0000000 0000000 PySpice-v0.3.2/PySpice/Unit/SiUnits.py 0000664 0000000 0000000 00000002756 13053355342 0017536 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
from . import Units
from .Units import __units__
####################################################################################################
for unit in __units__:
unit_name = unit.__spice_suffix__ + 'V'
setattr(Units, unit_name, type(unit_name, (unit,), dict()))
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Unit/Units.py 0000664 0000000 0000000 00000032076 13053355342 0017240 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
#
# - unit suffix
# - simplify .001
#
####################################################################################################
""" This module implements units.
"""
####################################################################################################
import numbers
import math
####################################################################################################
class Unit(numbers.Real):
__power__ = 0
__spice_suffix__ = ''
__unit_suffix__ = ''
##############################################
def __init__(self, value):
if isinstance(value, Unit):
if self.__power__ == value.__power__:
self._value = value._value
else:
self._value = float(value) / self.scale
elif isinstance(value, int):
self._value = value # to keep as int
else:
self._value = float(value)
##############################################
@property
def power(self):
return self.__power__
##############################################
@property
def scale(self):
return 10**self.__power__
##############################################
@property
def value(self):
return self._value
##############################################
def clone(self):
return self.__class__(self._value)
##############################################
def is_same_scale(self, other):
return isinstance(other, Unit) and self.__power__ == other.__power__
##############################################
def convert_value(self, other):
if self.is_same_scale(other):
return other._value
else:
return float(other) / self.scale
##############################################
def convert(self, other):
return self.__class__(self.convert_value(other))
##############################################
def __int__(self):
return int(self._value * self.scale)
##############################################
def __float__(self):
return float(self._value * self.scale)
##############################################
def __str__(self):
return str(self._value) + self.__spice_suffix__
##############################################
def __bool__(self):
"""True if self != 0. Called for bool(self)."""
return self._value != 0
##############################################
def __add__(self, other):
"""self + other"""
new_obj = self.clone()
new_obj._value += self.convert_value(other)
return new_obj
##############################################
def __iadd__(self, other):
"""self += other"""
self._value += self.convert_value(other)
return self
##############################################
def __radd__(self, other):
"""other + self"""
return other.__add__(self)
##############################################
def __neg__(self):
"""-self"""
return self.__class__(-self._value)
##############################################
def __pos__(self):
"""+self"""
return self.clone()
##############################################
def __sub__(self, other):
"""self - other"""
new_obj = self.clone()
new_obj._value -= self.convert_value(other)
return new_obj
##############################################
def __isub__(self, other):
"""self -= other"""
self._value -= self.convert_value(other)
return self
##############################################
def __rsub__(self, other):
"""other - self"""
return other.__sub__(self)
##############################################
def __mul__(self, other):
"""self * other"""
new_obj = self.clone()
new_obj._value *= float(other)
return new_obj
##############################################
def __imul__(self, other):
"""self *= other"""
self._value *= self.convert_value(other)
return self
##############################################
def __rmul__(self, other):
"""other * self"""
return other.__mul__(self)
##############################################
def __floordiv__(self, other):
"""self // other """
new_obj = self.clone()
new_obj._value //= float(other)
return new_obj
##############################################
def __ifloordiv__(self, other):
"""self //= other """
self._value //= float(other)
return self
##############################################
def __rfloordiv__(self, other):
"""other // self"""
return other.__floordiv__(self)
##############################################
def __truediv__(self, other):
"""self / other"""
new_obj = self.clone()
new_obj._value /= float(other)
return new_obj
##############################################
def __itruediv__(self, other):
"""self /= other"""
self._value /= float(other)
return self
##############################################
def __rtruediv__(self, other):
"""other / self"""
return other.__div__(self)
##############################################
def __pow__(self, exponent):
"""self**exponent; should promote to float or complex when necessary."""
new_obj = self.clone()
new_obj._value **= float(exponent)
return new_obj
##############################################
def __ipow__(self, exponent):
self._value **= float(exponent)
return self
##############################################
def __rpow__(self, base):
"""base ** self"""
raise NotImplementedError
##############################################
def __abs__(self):
"""Returns the Real distance from 0. Called for abs(self)."""
return self.__class__(abs(self._value))
##############################################
def __eq__(self, other):
"""self == other"""
return float(self) == float(other)
##############################################
def __ne__(self, other):
"""self != other"""
# The default __ne__ doesn't negate __eq__ until 3.0.
return not (self == other)
##############################################
def __trunc__(self):
"""trunc(self): Truncates self to an Integral.
Returns an Integral i such that:
* i>0 iff self>0;
* abs(i) <= abs(self);
* for any Integral j satisfying the first two conditions,
abs(i) >= abs(j) [i.e. i has "maximal" abs among those].
i.e. "truncate towards 0".
"""
raise NotImplementedError
##############################################
def __divmod__(self, other):
"""divmod(self, other): The pair (self // other, self % other).
Sometimes this can be computed faster than the pair of
operations.
"""
return (self // other, self % other)
##############################################
def __rdivmod__(self, other):
"""divmod(other, self): The pair (self // other, self % other).
Sometimes this can be computed faster than the pair of
operations.
"""
return (other // self, other % self)
##############################################
def __mod__(self, other):
"""self % other"""
raise NotImplementedError
##############################################
def __rmod__(self, other):
"""other % self"""
raise NotImplementedError
##############################################
def __lt__(self, other):
"""self < other
< on Reals defines a total ordering, except perhaps for NaN."""
return float(self) < float(other)
##############################################
def __le__(self, other):
"""self <= other"""
return float(self) <= float(other)
##############################################
def __ceil__(self):
return math.ceil(float(self))
##############################################
def __floor__(self):
return math.floor(float(self))
##############################################
def __round__(self):
return round(float(self))
##############################################
def inverse(self, the_class=None):
inverse = 1. / float(self)
if the_class is None:
return self.__class__(inverse / self.scale) # Fixme: to func?
else:
return the_class(inverse)
##############################################
def canonise(self):
# log10(10**n) = n log10(1) = 0 log10(10**-n) = -n log10(0) = -oo
try:
abs_value = abs(float(self))
log = math.log(abs_value)/math.log(1000)
# if abs_value >= 1:
# power = 3 * int(log)
# else:
# if log - int(log): # frac
# power = 3 * (int(log) -1)
# else:
# power = 3 * int(log)
power = int(log)
if abs_value < 1 and (log - int(log)):
power -= 1
power *= 3
unit = __power_to_unit__[power]
return unit(float(self) / 10**power)
except:
return self
####################################################################################################
class tera(Unit):
""" T Tera 1e12 """
__power__ = 12
__spice_suffix__ = 'T'
class giga(Unit):
""" G Giga 1e9 """
__power__ = 9
__spice_suffix__ = 'G'
class mega(Unit):
""" Meg Mega 1e6 """
__power__ = 6
__spice_suffix__ = 'Meg'
class kilo(Unit):
""" K Kilo 1e3 """
__power__ = 3
__spice_suffix__ = 'k'
class milli(Unit):
""" m milli 1e-3 """
__power__ = -3
__spice_suffix__ = 'm'
class micro(Unit):
""" u micro 1e-6 """
__power__ = -6
__spice_suffix__ = 'u'
class nano(Unit):
""" n nano 1e-9 """
__power__ = -9
__spice_suffix__ = 'n'
class pico(Unit):
""" p pico 1e-12 """
__power__ = -12
__spice_suffix__ = 'p'
class femto(Unit):
""" f femto 1e-15 """
__power__ = -15
__spice_suffix__ = 'f'
# class mil(Unit):
# """ mil Mil 25.4e-6 """
# __scale__ = 25.4e-6
# __spice_suffix__ = 'mil'
__units__ = (tera,
giga,
mega,
kilo,
Unit,
milli,
micro,
nano,
pico,
femto,
)
__power_to_unit__ = {unit.__power__:unit for unit in __units__}
####################################################################################################
class Frequency(Unit):
""" This class implements a frequency unit. """
##############################################
@property
def period(self):
r""" Return the period :math:`T = \frac{1}{f}`. """
return self.inverse(Period)
##############################################
@property
def pulsation(self):
r""" Return the pulsation :math:`\omega = 2\pi f`. """
return float(self * 2 * math.pi)
####################################################################################################
class Period(Unit):
""" This class implements a period unit. """
##############################################
@property
def frequency(self):
r""" Return the period :math:`f = \frac{1}{T}`. """
return self.inverse(Frequency)
##############################################
@property
def pulsation(self):
r""" Return the pulsation :math:`\omega = \frac{2\pi}{T}`. """
return self.frequency.pulsation
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/Unit/__init__.py 0000664 0000000 0000000 00000002156 13053355342 0017671 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/PySpice/__init__.py 0000664 0000000 0000000 00000002156 13053355342 0016752 0 ustar 00root root 0000000 0000000 ####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/README.html 0000664 0000000 0000000 00000046306 13053355342 0015115 0 ustar 00root root 0000000 0000000
Added an example to show how to use the NgSpice Shared Simulation Mode.
Completed the Spice netlist parser and added examples, we could now use a schematic editor
to define the circuit. The program cir2py translates a circuit file to Python.
Introduction
PySpice is a Python 3 library which interplay with Berkeley SPICE, the industrial circuit
simulator reference.
SPICE (Simulation Program with Integrated Circuit Emphasis) was developed at the Electronics
Research Laboratory of the University of California, Berkeley in 1973 by Laurence Nagel with
direction from his research advisor, Prof. Donald Pederson. Then Spice emerged as an industrial
standard through its descendants and is still the reference 40 years later.
PySpice is born as a personal project to relearn electronics where circuit simulation is a part of
this goal. Since I use the Python language every day, I quickly feel the need to plug SPICE and Python.
The aim of PySpice is to address several needs:
SPICE language is fine to describe circuits, but it lacks a real language for circuit
steering. By contrast Python is a powerful high level, oriented object and dynamic language which
is perfectly suited for steering and reusing circuit. But it comes at the price its more general
syntax is less fluent than SPICE for circuit description.
Ngspice provides some extension to Berkeley SPICE for data analysis, but its interactive
environment or TCL module are now outdated. By contrast Python has scientific framework like
Numpy and Matplotlib that compete with Matlab.
Ngspice source code is derived from Berkeley SPICE and thus has a very old basis. Moreover the
sources are poorly documented. So it is really difficult to understand how it works and modify
it. PySpice could serve to easily experiment extension.
As opposite to other SPICE derivatives, PySpice focus on programming and not on graphical user
interface. Thus it doesn't feature a schematic capture editor and we cannot pickup a node or an
element and plot the associated waveform. Moreover we can notice the Modelica language treats
diagrams as annotations. A choice which I consider judicious. Thus we can imagine to follow the
same principle and extend PySpice later.
an oriented-object API to describe circuit in a way similar to SPICE
a library and model manager that index recursively a directory
an (experimental) SPICE netlist parser. Kicad can be used as schematic editor to simplify the
netlist writing.
a circuit can be simulated using a subprocess (aka server mode) or using the NgSpice shared library,
NgSpice vectors are converted to Numpy array
the NgSpice shared library permits to plug voltage/current sources from Python to NgSpice and vice versa.
some data analysis add-ons
Since PySpice is born with a learning goal, many examples are provided with the sources. These
examples could serve as learning materials. A tool to generate an HTML and PDF documentation is
included in the tools directory. This tool could be extended to generate IPython Notebook as well.
Planned Features
These features are planned in the future:
implement a basic simulator featuring passive element like resistor, capacitor and inductor.
implement a Modelica backend. Modelica is a very interesting solution for transient analysis.
Advertisements
Users should be aware of these advertisements.
Warning
The API is quite unstable until now. Some efforts is made to have a smooth API.
Warning
Ngspice and PySpice are provided without any warranty. Thus use it with care for real
design. Best is to cross check the simulation using an industrial grade simulator.
Warning
Simulation is a tool and not a perfect representation of the real world.
Installation
The installation of PySpice by itself is quite simple. However it will be easier to get the
dependencies on a Linux desktop.
Usually Ngspice is available as a package in the major Linux distributions. But I recommend to check
the compilation options before to use it extensively. For example the Fedora package enables too
many experimental codes that have side effects. The recommended way to compile Ngspice is given in
the manual and the INSTALLATION file. Ngspice is an example of complex software where we should
not enable everything without care.
Warning
For the following, the compilation option --enable-ndev is known to broke the server mode.
Then to build and install PySpice run these commands:
python setup.py build
python setup.py install
PySpice-v0.3.2/README.rst 0000664 0000000 0000000 00000024007 13053355342 0014753 0 ustar 00root root 0000000 0000000 .. -*- Mode: rst -*-
.. -*- Mode: rst -*-
..
|PySpiceUrl|
|PySpiceHomePage|_
|PySpiceDoc|_
|PySpice@github|_
|PySpice@readthedocs|_
|PySpice@readthedocs-badge|
|PySpice@pypi|_
.. |ohloh| image:: https://www.openhub.net/accounts/230426/widgets/account_tiny.gif
:target: https://www.openhub.net/accounts/fabricesalvaire
:alt: Fabrice Salvaire's Ohloh profile
:height: 15px
:width: 80px
.. |PySpiceUrl| replace:: http://fabricesalvaire.github.io/PySpice
.. |PySpiceHomePage| replace:: PySpice Home Page
.. _PySpiceHomePage: http://fabricesalvaire.github.io/PySpice
.. |PySpiceDoc| replace:: PySpice Documentation
.. _PySpiceDoc: http://pyspice.readthedocs.org/en/latest
.. |PySpice@readthedocs-badge| image:: https://readthedocs.org/projects/pyspice/badge/?version=latest
:target: http://pyspice.readthedocs.org/en/latest
.. |PySpice@github| replace:: https://github.com/FabriceSalvaire/PySpice
.. .. _PySpice@github: https://github.com/FabriceSalvaire/PySpice
.. |PySpice@readthedocs| replace:: http://pyspice.readthedocs.org
.. .. _PySpice@readthedocs: http://pyspice.readthedocs.org
.. |PySpice@pypi| replace:: https://pypi.python.org/pypi/PySpice
.. .. _PySpice@pypi: https://pypi.python.org/pypi/PySpice
.. |Build Status| image:: https://travis-ci.org/FabriceSalvaire/PySpice.svg?branch=master
:target: https://travis-ci.org/FabriceSalvaire/PySpice
:alt: PySpice build status @travis-ci.org
.. |Pypi Download| image:: https://img.shields.io/pypi/dm/PySpice.svg
:target: https://pypi.python.org/pypi/PySpice
:alt: PySpice Download per month
.. |Pypi Version| image:: https://img.shields.io/pypi/v/PySpice.svg
:target: https://pypi.python.org/pypi/PySpice
:alt: PySpice last version
.. |Pypi License| image:: https://img.shields.io/pypi/l/PySpice.svg
:target: https://pypi.python.org/pypi/PySpice
:alt: PySpice license
.. |Pypi Format| image:: https://img.shields.io/pypi/format/PySpice.svg
:target: https://pypi.python.org/pypi/PySpice
:alt: PySpice format
.. |Pypi Python Version| image:: https://img.shields.io/pypi/pyversions/PySpice.svg
:target: https://pypi.python.org/pypi/PySpice
:alt: PySpice python version
.. coverage test
.. https://img.shields.io/pypi/status/Django.svg
.. https://img.shields.io/github/stars/badges/shields.svg?style=social&label=Star
.. End
.. -*- Mode: rst -*-
.. |Ngspice| replace:: Ngspice
.. _Ngspice: http://ngspice.sourceforge.net
.. |Python| replace:: Python
.. _Python: http://python.org
.. |PyPI| replace:: PyPI
.. _PyPI: https://pypi.python.org/pypi
.. |Numpy| replace:: Numpy
.. _Numpy: http://www.numpy.org
.. |Matplotlib| replace:: Matplotlib
.. _Matplotlib: http://matplotlib.org
.. |CFFI| replace:: CFFI
.. _CFFI: http://cffi.readthedocs.org/en/latest/
.. |IPython| replace:: IPython
.. _IPython: http://ipython.org
.. |Sphinx| replace:: Sphinx
.. _Sphinx: http://sphinx-doc.org
.. |Modelica| replace:: Modelica
.. _Modelica: http://www.modelica.org
.. |Kicad| replace:: Kicad
.. _Kicad: http://www.kicad-pcb.org
.. |Circuit_macros| replace:: Circuit_macros
.. _Circuit_macros: http://ece.uwaterloo.ca/~aplevich/Circuit_macros
.. End
=========
PySpice
=========
The official PySpice Home Page is located at |PySpiceUrl|
The latest documentation build from the git repository is available at readthedocs.org |PySpice@readthedocs-badge|
Written by `Fabrice Salvaire `_.
|Build Status|
|Pypi License|
|Pypi Python Version|
|Pypi Version|
|Pypi Format|
|Pypi Download|
-----
.. -*- Mode: rst -*-
======
News
======
V0.3.2
------
* fixed CCCS and CCVS
V0.3.1
------
* fixed ngspice shared
V0.3.0
------
* Added an example to show how to use the NgSpice Shared Simulation Mode.
* Completed the Spice netlist parser and added examples, we could now use a schematic editor
to define the circuit. The program *cir2py* translates a circuit file to Python.
.. End
.. -*- Mode: rst -*-
==============
Introduction
==============
PySpice is a |Python|_ 3 library which interplay with Berkeley SPICE, the industrial circuit
simulator reference.
SPICE (Simulation Program with Integrated Circuit Emphasis) was developed at the Electronics
Research Laboratory of the University of California, Berkeley in 1973 by Laurence Nagel with
direction from his research advisor, Prof. Donald Pederson. Then Spice emerged as an industrial
standard through its descendants and is still the reference 40 years later.
PySpice is born as a personal project to relearn electronics where circuit simulation is a part of
this goal. Since I use the Python language every day, I quickly feel the need to plug SPICE and Python.
The aim of PySpice is to address several needs:
* SPICE language is fine to describe circuits, but it lacks a real language for circuit
steering. By contrast Python is a powerful high level, oriented object and dynamic language which
is perfectly suited for steering and reusing circuit. But it comes at the price its more general
syntax is less fluent than SPICE for circuit description.
* Ngspice provides some extension to Berkeley SPICE for data analysis, but its interactive
environment or TCL module are now outdated. By contrast Python has scientific framework like
|Numpy|_ and |Matplotlib|_ that compete with Matlab.
* Ngspice source code is derived from Berkeley SPICE and thus has a very old basis. Moreover the
sources are poorly documented. So it is really difficult to understand how it works and modify
it. PySpice could serve to easily experiment extension.
As opposite to other SPICE derivatives, PySpice focus on programming and not on graphical user
interface. Thus it doesn't feature a schematic capture editor and we cannot pickup a node or an
element and plot the associated waveform. Moreover we can notice the |Modelica|_ language treats
diagrams as annotations. A choice which I consider judicious. Thus we can imagine to follow the
same principle and extend PySpice later.
.. -*- Mode: rst -*-
==========
Features
==========
The main features of PySpice are:
* actually PySpice only supports |Ngspice|_
* an oriented-object API to describe circuit in a way similar to SPICE
* a library and model manager that index recursively a directory
* an (experimental) SPICE netlist parser. |Kicad|_ can be used as schematic editor to simplify the
netlist writing.
* a circuit can be simulated using a subprocess (aka server mode) or using the NgSpice shared library,
NgSpice vectors are converted to Numpy array
* the NgSpice shared library permits to plug voltage/current sources from Python to NgSpice and vice versa.
* some data analysis add-ons
Since PySpice is born with a learning goal, many examples are provided with the sources. These
examples could serve as learning materials. A tool to generate an HTML and PDF documentation is
included in the *tools* directory. This tool could be extended to generate IPython Notebook as well.
..
* an incomplete SPICE parser (mainly used for the library and model indexer)
* a circuit can be simulated using a subprocess (aka server mode) or using the NgSpice shared
library, NgSpice vectors are converted to Numpy array the NgSpice shared library permits to interact
with the simulator and provides Python callback for external voltage and current source
* implement a SPICE to Python converted using the parser. It could be used for the following
workflow: quick circuit sketching using > SPICE netlist > spice2python > PySpice which
could help for complex circuit.
.. end
==================
Planned Features
==================
These features are planned in the future:
* implement a basic simulator featuring passive element like resistor, capacitor and inductor.
* implement a Modelica backend. |Modelica|_ is a very interesting solution for transient analysis.
================
Advertisements
================
Users should be aware of these advertisements.
.. Warning:: The API is quite unstable until now. Some efforts is made to have a smooth API.
.. Warning:: Ngspice and PySpice are provided without any warranty. Thus use it with care for real
design. Best is to cross check the simulation using an industrial grade simulator.
.. Warning:: Simulation is a tool and not a perfect representation of the real world.
.. End
.. -*- Mode: rst -*-
.. _installation-page:
==============
Installation
==============
The installation of PySpice by itself is quite simple. However it will be easier to get the
dependencies on a Linux desktop.
Dependencies
------------
PySpice requires the following dependencies:
* |Python|_ 3
* |Numpy|_
* |Matplotlib|_
* |Ngspice|_
* |CFFI|_ (only required for Ngspice shared)
Also it is recommanded to have these Python modules:
* |IPython|_
* pip
* virtualenv
For development, you will need in addition:
* |Sphinx|_
* circuit_macros and a LaTeX environment
Ngspice Compilation
-------------------
Usually Ngspice is available as a package in the major Linux distributions. But I recommend to check
the compilation options before to use it extensively. For example the Fedora package enables too
many experimental codes that have side effects. The recommended way to compile Ngspice is given in
the manual and the ``INSTALLATION`` file. Ngspice is an example of complex software where we should
not enable everything without care.
.. :file:`INSTALLATION`
.. warning::
For the following, the compilation option **--enable-ndev** is known to broke the server mode.
Installation from PyPi Repository
---------------------------------
PySpice is made available on the |Pypi|_ repository at |PySpice@pypi|
Run this command to install the last release:
.. code-block:: sh
pip install PySpice
Installation from Source
------------------------
The PySpice source code is hosted at |PySpice@github|
To clone the Git repository, run this command in a terminal:
.. code-block:: sh
git clone git@github.com:FabriceSalvaire/PySpice.git
Then to build and install PySpice run these commands:
.. code-block:: sh
python setup.py build
python setup.py install
.. End
.. End
PySpice-v0.3.2/README.txt 0000664 0000000 0000000 00000001070 13053355342 0014755 0 ustar 00root root 0000000 0000000 .. -*- Mode: rst -*-
.. include:: project-links.txt
.. include:: abbreviation.txt
=========
PySpice
=========
The official PySpice Home Page is located at |PySpiceUrl|
The latest documentation build from the git repository is available at readthedocs.org |PySpice@readthedocs-badge|
Written by `Fabrice Salvaire `_.
|Build Status|
|Pypi License|
|Pypi Python Version|
|Pypi Version|
|Pypi Format|
|Pypi Download|
-----
.. include:: news.txt
.. include:: introduction.txt
.. include:: installation.rst
.. End
PySpice-v0.3.2/bin/ 0000775 0000000 0000000 00000000000 13053355342 0014031 5 ustar 00root root 0000000 0000000 PySpice-v0.3.2/bin/cir2py 0000775 0000000 0000000 00000004665 13053355342 0015202 0 ustar 00root root 0000000 0000000 #! /usr/bin/env python3
####################################################################################################
#
# PySpice - A Spice Package for Python
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
####################################################################################################
####################################################################################################
import argparse
####################################################################################################
# import PySpice.Logging.Logging as Logging
# logger = Logging.setup_logging()
####################################################################################################
from PySpice.Spice.Parser import SpiceParser
####################################################################################################
parser = argparse.ArgumentParser(description='Convert a circuit file to PySpice')
parser.add_argument('circuit_file', # metavar='circuit_file',
help='.cir file')
parser.add_argument('-o', '--output',
default=None,
help='Output file')
parser.add_argument('--ground',
type=int,
default=0,
help='Ground node')
args = parser.parse_args()
####################################################################################################
parser = SpiceParser(path=args.circuit_file)
circuit = parser.to_python_code(ground=args.ground)
if args.output is not None:
with open(args.output, 'w') as f:
f.write(circuit)
else:
print(circuit)
####################################################################################################
#
# End
#
####################################################################################################
PySpice-v0.3.2/bower/ 0000775 0000000 0000000 00000000000 13053355342 0014377 5 ustar 00root root 0000000 0000000 PySpice-v0.3.2/bower/bower.json 0000664 0000000 0000000 00000000716 13053355342 0016414 0 ustar 00root root 0000000 0000000 {
"name": "PySpice",
"version": "0.1.0",
"homepage": "https://github.com/FabriceSalvaire/PySpice",
"authors": [
"Fabrice Salvaire "
],
"description": "A Spice Package for Python",
"main": "",
"keywords": [
""
],
"license": "GPLv3",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"MathJax": "~2.5.3",
"jquery": "~2.1.4"
}
}
PySpice-v0.3.2/doc/ 0000775 0000000 0000000 00000000000 13053355342 0014026 5 ustar 00root root 0000000 0000000 PySpice-v0.3.2/doc/sphinx/ 0000775 0000000 0000000 00000000000 13053355342 0015337 5 ustar 00root root 0000000 0000000 PySpice-v0.3.2/doc/sphinx/Makefile.sphinx 0000664 0000000 0000000 00000011344 13053355342 0020312 0 ustar 00root root 0000000 0000000 # -*- Makefile -*-
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
CLIENTSOPTS = -D master_doc=contents-client
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
htmlclient:
$(SPHINXBUILD) -b html $(CLIENTSOPTS) $(ALLSPHINXOPTS) $(BUILDDIR)/html-client
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html-client."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GVLab.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GVLab.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/GVLab"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GVLab"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
# End
PySpice-v0.3.2/doc/sphinx/make-html 0000775 0000000 0000000 00000000061 13053355342 0017141 0 ustar 00root root 0000000 0000000 #! /bin/bash
make -f Makefile.sphinx html
# End PySpice-v0.3.2/doc/sphinx/source/ 0000775 0000000 0000000 00000000000 13053355342 0016637 5 ustar 00root root 0000000 0000000 PySpice-v0.3.2/doc/sphinx/source/_static/ 0000775 0000000 0000000 00000000000 13053355342 0020265 5 ustar 00root root 0000000 0000000 PySpice-v0.3.2/doc/sphinx/source/_static/hidecode.js 0000664 0000000 0000000 00000001523 13053355342 0022370 0 ustar 00root root 0000000 0000000 $(document).ready(function() {
/* Add a [>>>] button on the top-right corner of getthecode div to show the code */
var div = $('.getthecode-header')
var button_text = 'Show/Hide the code';
var button_styles = {
'cursor':'pointer',
'float': 'right',
'text-size': '75%',
'font-family': 'monospace'
}
div.each(function(index) {
var jthis = $(this);
var button = $('>>>');
button.css(button_styles)
button.attr('title', button_text);
jthis.append(button);
jthis.parent().find('.highlight-python').toggle();
});
// define the behavior of the button when it's clicked
$('.show-code-button').click(function() {
var button = $(this);
button.parent().parent().find('.highlight-python').toggle();
});
});
PySpice-v0.3.2/doc/sphinx/source/_static/jquery.min.js 0000664 0000000 0000000 00000244634 13053355342 0022741 0 ustar 00root root 0000000 0000000 /*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){
return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"