diff --git a/PySpice/Spice/BasicElement.py b/PySpice/Spice/BasicElement.py
index ddc01b201..e4470d3d6 100644
--- a/PySpice/Spice/BasicElement.py
+++ b/PySpice/Spice/BasicElement.py
@@ -99,7 +99,8 @@
from ..Tools.StringTools import str_spice, join_list, join_dict
from ..Unit import U_m, U_s, U_A, U_V, U_Degree, U_Ω, U_F, U_H, U_Hz
-from .Netlist import (Element, AnyPinElement, FixedPinElement, NPinElement, OptionalPin)
+from .Netlist import (Element, AnyPinElement, FixedPinElement, NPinElement,
+ OptionalPin, Pin, PinDefinition)
from .ElementParameter import (
# KeyValueParameter,
BoolKeyParameter,
@@ -116,6 +117,8 @@
ModelPositionalParameter,
)
+import SchemDraw as schem
+
####################################################################################################
class DipoleElement(FixedPinElement):
@@ -153,12 +156,11 @@ class SubCircuitElement(NPinElement):
##############################################
- def __init__(self, netlist, name, subcircuit_name, *nodes, **parameters):
-
- super().__init__(netlist, name, nodes, subcircuit_name)
+ def __init__(self, netlist, name, subcircuit_name, *nodes, **kwargs):
+ schematic_kwargs = kwargs.pop('schematic_kwargs', {})
# Fixme: match parameters to subcircuit
- self.parameters = parameters
+ self.parameters = kwargs
# Fixme: investigate
# for key, value in parameters.items():
@@ -166,6 +168,14 @@ def __init__(self, netlist, name, subcircuit_name, *nodes, **parameters):
# parameter.__set__(self, value)
# self.optional_parameters[key] = parameter
# setattr(self, key, parameter)
+
+ subcircuit = netlist._subcircuits.get(subcircuit_name)
+
+ self._pins = [Pin(self, PinDefinition(position, name=subcircuit.__pins__[position]), netlist.get_node(node, True))
+ for position, node in enumerate(nodes)]
+
+ super().__init__(netlist, name, nodes, subcircuit_name,
+ schematic_kwargs=schematic_kwargs)
##############################################
@@ -239,6 +249,8 @@ class Resistor(DipoleElement):
__alias__ = 'R'
__prefix__ = 'R'
+
+ schematic = schem.elements.RES
resistance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_Ω)
ac = FloatKeyParameter('ac', unit=U_Ω)
@@ -761,6 +773,8 @@ class VoltageSource(DipoleElement):
__alias__ = 'V'
__prefix__ = 'V'
+
+ schematic = schem.elements.SOURCE_V
# Fixme: ngspice manual doesn't describe well the syntax
dc_value = FloatPositionalParameter(position=0, key_parameter=False, unit=U_V)
diff --git a/PySpice/Spice/Netlist.py b/PySpice/Spice/Netlist.py
index 462db633a..ba6bff4b2 100644
--- a/PySpice/Spice/Netlist.py
+++ b/PySpice/Spice/Netlist.py
@@ -82,7 +82,9 @@ def __init__(self, **kwargs):
import logging
import os
-# import networkx
+import networkx
+import SchemDraw
+import numpy as np
####################################################################################################
@@ -337,14 +339,14 @@ class Resistor(metaclass=ElementParameterMetaClass):
"""
- #: Dictionary for SPICE prefix -> [cls,]
+ #: Dictionary for SPICE prefix -> [cls,]
__classes__ = {}
_logger = _module_logger.getChild('ElementParameterMetaClass')
##############################################
- def __new__(meta_cls, class_name, base_classes, namespace):
+ def __new__(cls, class_name, base_classes, namespace):
# __new__ is called for the creation of a class depending of this metaclass, i.e. at module loading
# It customises the namespace of the new class
@@ -428,15 +430,15 @@ def getter(self):
else:
_module_logger.debug("{} don't define a __pins__ attribute".format(class_name))
- return type.__new__(meta_cls, class_name, base_classes, namespace)
+ return type.__new__(cls, class_name, base_classes, namespace)
##############################################
- def __init__(meta_cls, class_name, base_classes, namespace):
+ def __init__(cls, class_name, base_classes, namespace):
# __init__ is called after the class is created (__new__)
- type.__init__(meta_cls, class_name, base_classes, namespace)
+ type.__init__(cls, class_name, base_classes, namespace)
# Collect basic element classes
if '__prefix__' in namespace:
@@ -444,9 +446,9 @@ def __init__(meta_cls, class_name, base_classes, namespace):
if prefix is not None:
classes = ElementParameterMetaClass.__classes__
if prefix in classes:
- classes[prefix].append(meta_cls)
+ classes[prefix].append(cls)
else:
- classes[prefix] = [meta_cls]
+ classes[prefix] = [cls]
##############################################
@@ -454,33 +456,37 @@ def __init__(meta_cls, class_name, base_classes, namespace):
# e.g. Resistor.number_of_pins or Resistor.__class__.number_of_pins
@property
- def number_of_pins(cls):
+ def number_of_pins(self):
#! Fixme: many pins ???
- number_of_pins = len(cls.__pins__)
- if cls.__number_of_optional_pins__:
- return slice(number_of_pins - cls.__number_of_optional_pins__, number_of_pins +1)
+ number_of_pins = len(self.__pins__)
+ if self.__number_of_optional_pins__:
+ return slice(number_of_pins - self.__number_of_optional_pins__, number_of_pins +1)
else:
return number_of_pins
@property
- def number_of_positional_parameters(cls):
- return len(cls.__positional_parameters__)
+ def number_of_positional_parameters(self):
+ return len(self.__positional_parameters__)
@property
- def positional_parameters(cls):
- return cls.__positional_parameters__
+ def positional_parameters(self):
+ return self.__positional_parameters__
@property
- def optional_parameters(cls):
- return cls.__optional_parameters__
+ def optional_parameters(self):
+ return self.__optional_parameters__
@property
- def parameters_from_args(cls):
- return cls.__parameters_from_args__
+ def parameters_from_args(self):
+ return self.__parameters_from_args__
@property
- def spice_to_parameters(cls):
- return cls.__spice_to_parameters__
+ def spice_to_parameters(self):
+ return self.__spice_to_parameters__
+
+# @property
+# def schematic(self):
+# return self.__schematic__
####################################################################################################
@@ -501,6 +507,8 @@ class Element(metaclass=ElementParameterMetaClass):
#: SPICE element prefix
__prefix__ = None
+
+ schematic = None
##############################################
@@ -510,6 +518,7 @@ def __init__(self, netlist, name, *args, **kwargs):
self._name = str(name)
self.raw_spice = ''
self.enabled = True
+ #self._pins = kwargs.pop('pins',())
# Process remaining args
if len(self.__parameters_from_args__) < len(args):
@@ -524,8 +533,10 @@ def __init__(self, netlist, name, *args, **kwargs):
elif key in self.__positional_parameters__ or key in self.__optional_parameters__:
setattr(self, key, value)
- self._pins = ()
- netlist._add_element(self)
+ schematic_kwargs = kwargs.pop('schematic_kwargs', {})
+ self.schematic_kwargs = schematic_kwargs
+
+ netlist._add_element(self, **schematic_kwargs)
##############################################
@@ -690,10 +701,13 @@ def __init__(self, netlist, name, *args, **kwargs):
raise NameError("Node '{}' is missing for element {}".format(pin_definition.name, self.name))
pin_definition_nodes.append((pin_definition, node))
- super().__init__(netlist, name, *args, **kwargs)
+
self._pins = [Pin(self, pin_definition, netlist.get_node(node, True))
for pin_definition, node in pin_definition_nodes]
+
+
+ super().__init__(netlist, name, *args, **kwargs)
##############################################
@@ -715,9 +729,6 @@ def __init__(self, netlist, name, nodes, *args, **kwargs):
super().__init__(netlist, name, *args, **kwargs)
- self._pins = [Pin(self, PinDefinition(position), netlist.get_node(node, True))
- for position, node in enumerate(nodes)]
-
##############################################
def copy_to(self, netlist):
@@ -826,6 +837,8 @@ def __init__(self):
self._ground_name = 0
self._nodes = {}
+ self._graph = networkx.Graph()
+ self._schematic = SchemDraw.Drawing()
self._ground_node = self._add_node(self._ground_name)
self._subcircuits = OrderedDict() # to keep the declaration order
@@ -834,7 +847,6 @@ def __init__(self):
self.raw_spice = ''
- # self._graph = networkx.Graph()
##############################################
@@ -890,6 +902,14 @@ def subcircuits(self):
@property
def subcircuit_names(self):
return self._subcircuits.keys()
+
+ @property
+ def graph(self):
+ return self._graph
+
+ @property
+ def schematic(self):
+ return self._schematic
##############################################
@@ -922,6 +942,10 @@ def _add_node(self, node_name):
if node_name not in self._nodes:
node = Node(self, node_name)
self._nodes[node_name] = node
+ self._graph.add_node(node, name=node_name)
+
+ #if(node_name == str(self._ground_name)):
+ # self.schematic.add(SchemDraw.elements.GND)
return node
else:
raise ValueError("Node {} is already defined".format(node_name))
@@ -960,12 +984,43 @@ def has_ground_node(self):
##############################################
- def _add_element(self, element):
+ def _add_element(self, element, **schematic_kwargs):
"""Add an element."""
-
if element.name not in self._elements:
self._elements[element.name] = element
+
+ if len(element.nodes) == 2:
+ self.graph.add_edge(element.nodes[0], element.nodes[1],
+ x=element, name=element.name)
+
+ #print(schematic_kwargs)
+ schematic = schematic_kwargs.pop('schematic', element.schematic)
+ if(schematic):
+ element.schematic = schematic
+ #label = schematic_kwargs.pop('label', element.name)
+ schematic_element = self.schematic.add(schematic,
+ **schematic_kwargs)
+ element.schematic_element = schematic_element
+
+ show_start = schematic_kwargs.pop('show_start', False)
+ show_end = schematic_kwargs.pop('show_end', False)
+
+ if(show_start):
+ start_label = schematic_kwargs.pop('start_label',{})
+
+ self.schematic.add(SchemDraw.elements.DOT_OPEN,
+ xy=schematic_element.start,
+ **start_label)
+
+ if(show_end):
+ end_label = schematic_kwargs.pop('end_label',{})
+ self.schematic.add(SchemDraw.elements.DOT_OPEN,
+ xy=schematic_element.end,
+ **end_label)
+ #if(element.pins[1].node == self.get_node(0, False)):
+ # self.schematic.add(SchemDraw.elements.GND)
+
else:
raise NameError("Element name {} is already defined".format(element.name))
@@ -1138,6 +1193,7 @@ class SubCircuitFactory(SubCircuit):
__name__ = None
__nodes__ = None
+ __pins__ = None
##############################################
diff --git a/PySpice/Spice/NgSpice/SimulationType.py b/PySpice/Spice/NgSpice/SimulationType.py
index e5df5da74..b896aebb7 100644
--- a/PySpice/Spice/NgSpice/SimulationType.py
+++ b/PySpice/Spice/NgSpice/SimulationType.py
@@ -81,3 +81,4 @@
)
SIMULATION_TYPE[28] = SIMULATION_TYPE[27]
+SIMULATION_TYPE[29] = SIMULATION_TYPE[28]
diff --git a/examples/ctia_readout/ctia_readout.ipynb b/examples/ctia_readout/ctia_readout.ipynb
new file mode 100644
index 000000000..c77ed304c
--- /dev/null
+++ b/examples/ctia_readout/ctia_readout.ipynb
@@ -0,0 +1,3020 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/usr/local/lib/python3.6/dist-packages/matplotlib/__init__.py:908: MatplotlibDeprecationWarning: The backend.qt4 rcParam was deprecated in version 2.2. In order to force the use of a specific Qt binding, either import that binding first, or set the QT_API environment variable.\n",
+ " mplDeprecation)\n",
+ "/usr/local/lib/python3.6/dist-packages/matplotlib/__init__.py:908: MatplotlibDeprecationWarning: The backend.qt4 rcParam was deprecated in version 2.2. In order to force the use of a specific Qt binding, either import that binding first, or set the QT_API environment variable.\n",
+ " mplDeprecation)\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\u001b[1;32m2018-11-26 18:08:39,871\u001b[0m - \u001b[1;34mPySpice.Doc.ExampleTools.find_libraries\u001b[0m - \u001b[1;31mINFO\u001b[0m - SPICE library path is ~/Projects/ROIC/src/PySpice/examples/libraries\n"
+ ]
+ }
+ ],
+ "source": [
+ "%matplotlib inline\n",
+ "%config InlineBackend.figure_format = 'svg'\n",
+ "####################################################################################################\n",
+ "\n",
+ "import os\n",
+ "\n",
+ "import numpy as np\n",
+ "import pylab as pb\n",
+ "import matplotlib.pyplot as plt\n",
+ "import matplotlib.ticker as ticker\n",
+ "import networkx as nx\n",
+ "import SchemDraw as schem\n",
+ "\n",
+ "####################################################################################################\n",
+ "import PySpice\n",
+ "import PySpice.Logging.Logging as Logging\n",
+ "logger = Logging.setup_logging()\n",
+ "\n",
+ "####################################################################################################\n",
+ "\n",
+ "\n",
+ "from PySpice.Doc.ExampleTools import find_libraries\n",
+ "from PySpice.Spice.Netlist import Circuit\n",
+ "from PySpice.Spice.Library import SpiceLibrary\n",
+ "from PySpice.Unit import *\n",
+ "from PySpice.Physics.SemiConductor import ShockleyDiode\n",
+ "from PySpice.Spice.Netlist import SubCircuitFactory\n",
+ "\n",
+ "os.environ['PySpiceLibraryPath'] = '~/Projects/ROIC/src/PySpice/examples/libraries'\n",
+ "\n",
+ "####################################################################################################\n",
+ "\n",
+ "libraries_path = find_libraries()\n",
+ "spice_library = SpiceLibrary(libraries_path)\n",
+ "####################################################################################################"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from math import pi\n",
+ "#cgs units\n",
+ "cm = 1.0\n",
+ "m = 100.0\n",
+ "mum = 1e-6*m\n",
+ "nm = 1e-9*m\n",
+ "g = 1.0\n",
+ "kg = 1000.0*g\n",
+ "\n",
+ "s = 1.0\n",
+ "ms = 1e-3*s\n",
+ "ns = 1e-9*s\n",
+ "mus = 1e-6*s\n",
+ "\n",
+ "Hertz = Hz = 1.0/s\n",
+ "\n",
+ "erg = g*cm**2/s**2\n",
+ "Joule = kg*m**2/s**2\n",
+ "\n",
+ "Watt = Joule/s\n",
+ "mW = 1e-3*Watt\n",
+ "\n",
+ "Coulomb = 1.0\n",
+ "Ampere = Coulomb/s\n",
+ "mA = 1e-3*Ampere\n",
+ "nA = 1e-9*Ampere\n",
+ "Kelvin = 1.0\n",
+ "\n",
+ "eps0 = 8.854187817620e-12*Ampere**2*s**4/kg/m**3\n",
+ "h_planck = 6.62606885e-27*erg*s\n",
+ "hbar_planck = h_planck/2.0/pi\n",
+ "q_e = 1.6021766208e-19*Coulomb\n",
+ "k_b = 1.38064852e-16*erg/Kelvin\n",
+ "\n",
+ "eV =q_e*Joule/Coulomb\n",
+ "Volt = Joule/Coulomb\n",
+ "mV = 1e-3*Volt\n",
+ "\n",
+ "Ohm = Volt/Ampere\n",
+ "kOhm = 1e3*Ohm\n",
+ "MOhm = 1e6*Ohm \n",
+ "\n",
+ "Farad = Coulomb/Volt\n",
+ "uF = 1e-6*Farad\n",
+ "nF = 1e-9*Farad\n",
+ "pF = 1e-12*Farad"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "PySpice.__file__"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "class BasicOperationalAmplifier(SubCircuitFactory):\n",
+ "\n",
+ " __name__ = 'BasicOperationalAmplifier'\n",
+ " __nodes__ = ('non_inverting_input', 'inverting_input', 'output')\n",
+ " __pins__ = ('plus', 'minus', 'out')\n",
+ "\n",
+ "\n",
+ " def __init__(self):\n",
+ "\n",
+ " super().__init__()\n",
+ "\n",
+ " # Input impedance\n",
+ " RIN = self.R('input', 'non_inverting_input', 'inverting_input', 10@u_MΩ,\n",
+ " schematic_kwargs={'schematic': schem.elements.RES,\n",
+ " 'label': r'$R_{in}$', 'd': 'up',\n",
+ " 'show_start': True, 'start_label': {'lftlabel': r'$v_+$'},\n",
+ " 'show_end': True, 'end_label': {'lftlabel': r'$v_-$'},\n",
+ " }\n",
+ " )\n",
+ " self.RIN = RIN\n",
+ " #self._schematic.add(schem.elements.RES,label=r'$R_in$')\n",
+ "\n",
+ " # dc gain=100k and pole1=100hz\n",
+ " # unity gain = dcgain x pole1 = 10MHZ\n",
+ " self.schematic.add(schem.elements.LINE, d ='right', l=1.0)\n",
+ " vcvs_in = self.VCVS('gain', 1, self.gnd, 'non_inverting_input', 'inverting_input', voltage_gain=kilo(100),\n",
+ " schematic_kwargs={'schematic': schem.elements.SOURCE_CONT_V,\n",
+ " 'anchor':'in1',#'d':'right',\n",
+ " 'show_end': True, 'end_label': {'toplabel': r'$v_1 = A(v_+ - v_-)$'},\n",
+ " #'rgtlabel': r'$A(v_+ - v_-)$',\n",
+ " #'l': RIN.schematic_element.dy,\n",
+ " })\n",
+ " self.vcvs_in = vcvs_in\n",
+ " self.schematic.add(schem.elements.GND, xy=vcvs_in.schematic_element.start)\n",
+ " self.schematic.add(schem.elements.DOT_OPEN, xy=vcvs_in.schematic_element.in2, rgtlabel=r'$v_+$')\n",
+ " \n",
+ " #self.schematic.add(schem.elements.LINE, xy=vcvs_in.schematic_element.end, d='right', l=1.5)\n",
+ " RP1 = self.R('P1', 1, 2, 1@u_kΩ,\n",
+ " schematic_kwargs={'schematic': schem.elements.RES,\n",
+ " 'd':'right',\n",
+ " 'xy': vcvs_in.schematic_element.end,\n",
+ " 'botlabel': r'$RP1$',\n",
+ " 'l':5.5\n",
+ " })\n",
+ " CP1 = self.C('P1', 2, self.gnd, 1.5915@u_uF, \n",
+ " schematic_kwargs={'schematic': schem.elements.CAP,\n",
+ " 'd':'down',\n",
+ " 'botlabel': r'$CP1$',\n",
+ " 'show_start': True, 'start_label': {'toplabel': r'$v_2$'},\n",
+ " })\n",
+ " self.schematic.add(schem.elements.GND, xy=CP1.schematic_element.end)\n",
+ "\n",
+ " # Output buffer and resistance\n",
+ " self.schematic.add(schem.elements.LINE, xy=CP1.schematic_element.start, d='right', l=2.0)\n",
+ " vcvs_out = self.VCVS('buffer', 3, self.gnd, 2, self.gnd, 1,\n",
+ " schematic_kwargs={'schematic': schem.elements.SOURCE_CONT_V,\n",
+ " 'anchor':'in1',#'d':'right',\n",
+ " 'show_end': True, 'end_label': {'toplabel': r'$v_3 = v_2$'}}\n",
+ " )\n",
+ " self.vcvs_out = vcvs_out\n",
+ " self.schematic.add(schem.elements.GND, xy=vcvs_out.schematic_element.start)\n",
+ " self.schematic.add(schem.elements.GND, xy=vcvs_out.schematic_element.in2)\n",
+ " \n",
+ " \n",
+ " ROUT = self.R('out', 3, 'output', 10@u_Ω,\n",
+ " schematic_kwargs={'schematic': schem.elements.RES,\n",
+ " 'label': r'$R_{out}$', 'd': 'right',\n",
+ " 'show_end': True, 'end_label': {'rgtlabel': r'$v_{o}$'},\n",
+ " 'l': 3.5\n",
+ " }\n",
+ " )\n",
+ " self.ROUT = ROUT\n",
+ " \n",
+ "opamp = BasicOperationalAmplifier()\n",
+ "opamp.schematic.draw()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "#r# For this purpose, we use the common high-speed diode 1N4148. The diode is driven by a variable\n",
+ "#r# voltage source through a limiting current resistance.\n",
+ "\n",
+ "#f# circuit_macros('diode-characteristic-curve-circuit.m4')\n",
+ "\n",
+ "circuit = Circuit('Capacitive Transimpedance Amplifier (CTIA) readout')\n",
+ "\n",
+ "#circuit.include(spice_library['1N4148'])\n",
+ "\n",
+ "\n",
+ "DD = circuit.D('sensor', 'anode', 'Vd',\n",
+ " schematic_kwargs={'schematic': schem.elements.PHOTODIODE, \n",
+ " 'd': 'left', 'show_end': True, 'end_label': {'lftlabel': r'$V_d$'},\n",
+ " 'flip': True,\n",
+ " 'botlabel': r'$D_d$'\n",
+ " }\n",
+ " )\n",
+ "#circuit.schematic.add(schem.elements.LINE, xy=DD.schematic_element.start, d ='down', l=1.5)\n",
+ "#CD = circuit.C('d', 'anode', 'Vd', 1@u_uF,\n",
+ "# schematic_kwargs={'schematic': schem.elements.CAP, 'botlabel': r'$C_d$',\n",
+ "# 'd': 'left', 'l': DD.schematic_element.dx}\n",
+ "# )\n",
+ "#circuit.schematic.add(schem.elements.LINE, xy=CD.schematic_element.end, to=DD.schematic_element.end)\n",
+ "\n",
+ "circuit.subcircuit(opamp)\n",
+ "seg = circuit.schematic.add(schem.elements.LINE, xy=DD.schematic_element.start, d ='right', l=2.0)\n",
+ "circuit.schematic.labelI(seg, arrowlen=1.0, reverse=False, arrowofst=0.5,\n",
+ " label=r'$i_S(t)$', top=True)\n",
+ "\n",
+ "XA = circuit.X('A', 'BasicOperationalAmplifier', 'Vcom', 'anode', 'Aout',\n",
+ " schematic_kwargs={'schematic': schem.elements.OPAMP, 'anchor': 'in1', \n",
+ " 'd': 'right', 'flip':False,\n",
+ " #'xy': CD.schematic_element.start\n",
+ " })\n",
+ "circuit.schematic.add(schem.elements.DOT, lftlabel =r'$V_{com}$', xy=XA.schematic_element.in2)\n",
+ "\n",
+ "circuit.schematic.add(schem.elements.LINE, xy=XA.schematic_element.in1, d ='up', l=2.0)\n",
+ "CINT = circuit.C('int', 'anode', 'Aout', 1@u_uF,\n",
+ " schematic_kwargs={'schematic': schem.elements.CAP, 'toplabel': r'$C_{int}$',\n",
+ " 'd': 'right', 'l': XA.schematic_element.dx}\n",
+ " )\n",
+ "circuit.schematic.add(schem.elements.LINE, xy=XA.schematic_element.out, d ='up', \n",
+ " to=CINT.schematic_element.end)\n",
+ "\n",
+ "\n",
+ "circuit.schematic.add(schem.elements.LINE, xy=CINT.schematic_element.start, d ='up', l=2.0)\n",
+ "RF = circuit.R('F', 'anode', 'Aout', 1@u_MOhm,\n",
+ " schematic_kwargs={'schematic': schem.elements.RES, 'toplabel': r'$R_F$',\n",
+ " 'd': 'right', 'l': XA.schematic_element.dx}\n",
+ " )\n",
+ "circuit.schematic.add(schem.elements.LINE, xy=CINT.schematic_element.end, d ='up', l=2.0)\n",
+ "\n",
+ "circuit.schematic.add(schem.elements.LINE, xy=XA.schematic_element.out, d ='right', l=2.0)\n",
+ "circuit.schematic.add(schem.elements.DOT_OPEN, label =r'$v_S(t)$')\n",
+ "\n",
+ "circuit.schematic.draw()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\\begin{gather}\n",
+ "v_C(t) \\equiv V_{com}-v_S(t) = V_C + \\frac{1}{C_{int}}\\int_0^{\\tau_{int}}{i_C(t)dt} \\\\\n",
+ "i_C = i_S - (V_{com}-v_S)/R_F\n",
+ "\\end{gather}\n",
+ "\n",
+ "\\begin{align}\n",
+ "v_S &= V_{com}-V_C-\\frac{1}{C_{int}}\\int_0^{\\tau_{int}}{i_C(t)dt} \\\\\n",
+ "&= V_{com}-V_C-\\frac{1}{C_{int}}\\int_0^{\\tau_{int}}{i_S(t)dt} + \\frac{1}{C_{int}}\\int_0^{\\tau_{int}}{\\frac{V_{com}-v_S}{R_F}dt} \\\\\n",
+ "&= V_{com}-V_C-\\frac{1}{C_{int}}\\int_0^{\\tau_{int}}{i_S(t)dt} + \\frac{V_{com}\\tau_{int}}{R_FC_{int}}\n",
+ "-\\frac{1}{R_FC_{int}}\\int_0^{\\tau_{int}}{v_S(t)dt}\n",
+ "\\end{align}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Thus the signal voltage $v_S$ is defined by the differential equation,\n",
+ "\n",
+ "\\begin{equation}\n",
+ "\\frac{dv_S}{dt} + \\frac{v_S}{R_FC_{int}} + \\frac{i_S}{C_{int}} = 0\n",
+ "\\end{equation}\n",
+ "\n",
+ "which can be solved as,\n",
+ "\n",
+ "\\begin{gather}\n",
+ "\\mu = e^{\\frac{t}{R_FC_{int}}} \\\\\n",
+ "\\mu\\left[v_S^\\prime + \\frac{v_S}{R_FC_{int}}\\right] + \\frac{\\mu i_S}{C_{int}} \\\\\n",
+ "(\\mu v_S)^\\prime = \\mu v_S^\\prime + \\mu^\\prime v_S \\\\\n",
+ "\\mu^\\prime = \\frac{\\mu}{R_FC_{int}} \\\\\n",
+ "(\\mu v_S)^\\prime + \\frac{\\mu i_S}{C_{int}} = 0 \\\\\n",
+ "v_S(\\tau_{int}) = v_S(0) -\\frac{e^{\\frac{-\\tau_{int}}{R_FC_{int}}}}{R_FC_{int}} \\int_0^{\\tau_{int}}{e^{\\frac{t}{R_FC_{int}}} R_F i_S dt}\\\\\n",
+ "v_S(\\tau_{int}) = v_S(0) -\\frac{1}{R_FC_{int}} \\int_0^{\\tau_{int}}{e^{\\frac{t-\\tau_{int}}{R_FC_{int}}} R_F i_S dt}\\\\\n",
+ "v_S(\\infty) \\equiv R_F i_S(\\infty) \\\\\n",
+ "\\lim_{\\tau_{int}\\to\\infty}\\int_0^{\\tau_{int}}{e^{\\frac{t-\\tau_{int}}{R_FC_{int}}} R_F i_S dt} = R_F C_{int} \\left[ v_S(0)-v_S(\\infty) \\right]\n",
+ "\\end{gather}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 54,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "def i_S(t):\n",
+ " t_off = np.argwhere(np.logical_or(t>i_S.t1, t<=i_S.t0))\n",
+ " t_on = np.argwhere(np.logical_and(t<=i_S.t1, t>i_S.t0))\n",
+ " i = np.zeros_like(t)\n",
+ " i[t_on] = i_S.val #i_S.tramp*t[t_pos]\n",
+ " \n",
+ " return i\n",
+ "\n",
+ "\n",
+ "R_F = 1.0*MOhm\n",
+ "C_INT = 0.1*nF\n",
+ "rc_delay = R_F*C_INT\n",
+ "\n",
+ "i_S.val = -100.0*nA\n",
+ "i_S.t0 = 0.0\n",
+ "i_S.t1 = 0.5*rc_delay\n",
+ "i_S.tramp = 0.1*mA/mus\n",
+ "\n",
+ "def v_S(tau_int, RF=R_F, CINT=C_INT, v0=0.0*Volt):\n",
+ " \n",
+ " v = np.zeros_like(tau_int)\n",
+ " for j, tau in enumerate(tau_int):\n",
+ " t = np.linspace(0,tau, 500)\n",
+ " i = i_S(t)\n",
+ " f = np.exp((t-tau)/RF/CINT)*RF*i\n",
+ " I = np.trapz(f,x=t)\n",
+ " v[j] = v0 - I/RF/CINT\n",
+ " \n",
+ " return v\n",
+ "\n",
+ "\n",
+ "t = np.linspace(0, 4.0*rc_delay,100)\n",
+ "\n",
+ "pb.plot(t/rc_delay, i_S(t)/nA, ls='--', c='b')\n",
+ "ax1 = pb.gca()\n",
+ "ax2 = pb.twinx(ax1)\n",
+ "tau_int = np.linspace(0.0, 4.0*rc_delay, 100)\n",
+ "pb.plot(tau_int/rc_delay, v_S(tau_int)/mV, c='b')\n",
+ "i_S.val = -200.0*nA\n",
+ "i_S.tramp = 0.2*mA/mus\n",
+ "ax1.plot(t/rc_delay, i_S(t)/nA, ls='--', c='r')\n",
+ "ax2.plot(tau_int/rc_delay, v_S(tau_int)/mV, c='r')\n",
+ "ax2.axvline(rc_delay/rc_delay, c='k', ls='--')\n",
+ "\n",
+ "ax1.set_xlabel('Time ($RC$)')\n",
+ "ax1.set_ylabel('$i_S$ ($nA$)')\n",
+ "ax2.set_ylabel('$v_S$ ($mV$)')\n",
+ "ax2.set_yscale('linear')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "np.trapz?"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/examples/diode/diode-characteristic-curve.ipynb b/examples/diode/diode-characteristic-curve.ipynb
new file mode 100644
index 000000000..9c0758bc5
--- /dev/null
+++ b/examples/diode/diode-characteristic-curve.ipynb
@@ -0,0 +1,2292 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%matplotlib inline\n",
+ "%config InlineBackend.figure_format = 'svg'\n",
+ "####################################################################################################\n",
+ "\n",
+ "import os\n",
+ "\n",
+ "import numpy as np\n",
+ "import matplotlib.pyplot as plt\n",
+ "import matplotlib.ticker as ticker\n",
+ "import networkx as nx\n",
+ "import SchemDraw as schem\n",
+ "\n",
+ "####################################################################################################\n",
+ "\n",
+ "import PySpice.Logging.Logging as Logging\n",
+ "logger = Logging.setup_logging()\n",
+ "\n",
+ "####################################################################################################\n",
+ "\n",
+ "from PySpice.Doc.ExampleTools import find_libraries\n",
+ "from PySpice.Spice.Netlist import Circuit\n",
+ "from PySpice.Spice.Library import SpiceLibrary\n",
+ "from PySpice.Unit import *\n",
+ "from PySpice.Physics.SemiConductor import ShockleyDiode\n",
+ "\n",
+ "os.environ['PySpiceLibraryPath'] = '~/Projects/ROIC/src/PySpice/examples/libraries'\n",
+ "\n",
+ "####################################################################################################\n",
+ "\n",
+ "libraries_path = find_libraries()\n",
+ "spice_library = SpiceLibrary(libraries_path)\n",
+ "####################################################################################################"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "#r# For this purpose, we use the common high-speed diode 1N4148. The diode is driven by a variable\n",
+ "#r# voltage source through a limiting current resistance.\n",
+ "\n",
+ "#f# circuit_macros('diode-characteristic-curve-circuit.m4')\n",
+ "\n",
+ "circuit = Circuit('Diode Characteristic Curve')\n",
+ "\n",
+ "circuit.include(spice_library['1N4148'])\n",
+ "\n",
+ "circuit.schematic.add(schem.elements.GND)\n",
+ "V =circuit.V('input', 'Vin', circuit.gnd, 10@u_V,\n",
+ " schematic_kwargs={'show_plus': True}\n",
+ " )\n",
+ "R = circuit.R('1', 'Vin', 'Vout', 1@u_Ω,\n",
+ " schematic_kwargs={'d':'right', 'show_minus': True}\n",
+ " ) # not required for simulation\n",
+ "X = circuit.X('D1', '1N4148', 'Vout', circuit.gnd,\n",
+ " schematic_kwargs={'schematic': schem.elements.DIODE, 'd':'down'},\n",
+ " )\n",
+ "circuit.schematic.add(schem.elements.GND)\n",
+ "circuit.schematic.draw()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [],
+ "source": [
+ "#pos = nx.spectral_layout(circuit.graph)\n",
+ "#nx.draw(circuit.graph, pos=pos, with_labels=True, node_size=1200, node_shape='s',\n",
+ "# width=2.0)\n",
+ "#edge_labels = nx.get_edge_attributes(circuit.graph,'name')\n",
+ "#nx.draw_networkx_edge_labels(circuit.graph, pos, edge_labels=edge_labels)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#r# We simulate the circuit at these temperatures: 0, 25 and 100 °C.\n",
+ "\n",
+ "# Fixme: Xyce ???\n",
+ "temperatures = [0, 25, 100]@u_Degree\n",
+ "analyses = {}\n",
+ "for temperature in temperatures:\n",
+ " simulator = circuit.simulator(temperature=temperature, nominal_temperature=temperature)\n",
+ " analysis = simulator.dc(Vinput=slice(-2, 5, .01))\n",
+ " analyses[float(temperature)] = analysis\n",
+ "\n",
+ "####################################################################################################"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Text(1,-100,'Forward Biased Region')"
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "#r# We plot the characteristic curve and compare it to the Shockley diode model:\n",
+ "#r#\n",
+ "#r# .. math::\n",
+ "#r#\n",
+ "#r# I_d = I_s \\left( e^{\\frac{V_d}{n V_T}} - 1 \\right)\n",
+ "#r#\n",
+ "#r# where :math:`V_T = \\frac{k T}{q}`\n",
+ "#r#\n",
+ "#r# In order to scale the reverse biased region, we have to do some hack with Matplotlib.\n",
+ "#r#\n",
+ "\n",
+ "silicon_forward_voltage_threshold = .7\n",
+ "\n",
+ "shockley_diode = ShockleyDiode(Is=4.0@u_nA, degree=25)\n",
+ "\n",
+ "def two_scales_tick_formatter(value, position):\n",
+ " if value >= 0:\n",
+ " return '{} mA'.format(value)\n",
+ " else:\n",
+ " return '{} nA'.format(value/100)\n",
+ "formatter = ticker.FuncFormatter(two_scales_tick_formatter)\n",
+ "\n",
+ "figure = plt.figure(1, (10, 5))\n",
+ "\n",
+ "axe = plt.subplot(121)\n",
+ "axe.set_title('1N4148 Characteristic Curve ')\n",
+ "axe.set_xlabel('Voltage [V]')\n",
+ "axe.set_ylabel('Current')\n",
+ "axe.grid()\n",
+ "axe.set_xlim(-2, 2)\n",
+ "axe.axvspan(-2, 0, facecolor='green', alpha=.2)\n",
+ "axe.axvspan(0, silicon_forward_voltage_threshold, facecolor='blue', alpha=.1)\n",
+ "axe.axvspan(silicon_forward_voltage_threshold, 2, facecolor='blue', alpha=.2)\n",
+ "#axe.set_ylim(-500, 750) # Fixme: round\n",
+ "#axe.yaxis.set_major_formatter(formatter)\n",
+ "Vd = analyses[25].Vout\n",
+ "# compute scale for reverse and forward region\n",
+ "forward_region = Vd >= 0@u_V\n",
+ "reverse_region = np.invert(forward_region)\n",
+ "scale = reverse_region*1e11 + forward_region*1e3\n",
+ "#?# check temperature\n",
+ "for temperature in temperatures:\n",
+ " analysis = analyses[float(temperature)]\n",
+ " axe.plot(Vd, np.abs(- analysis.Vinput))\n",
+ "axe.plot(Vd, np.abs(shockley_diode.I(Vd)), 'black')\n",
+ "axe.set_ylim(1e-9, 1e3)\n",
+ "axe.set_yscale('log')\n",
+ "axe.legend(['@ {}'.format(temperature)\n",
+ " for temperature in temperatures] + ['Shockley Diode Model Is = 4 nA'],\n",
+ " loc=2, fontsize=10)\n",
+ "axe.axvline(x=0, color='black')\n",
+ "axe.axhline(y=0, color='black')\n",
+ "axe.axvline(x=silicon_forward_voltage_threshold, color='red')\n",
+ "axe.text(-1, -100, 'Reverse Biased Region', ha='center', va='center')\n",
+ "axe.text( 1, -100, 'Forward Biased Region', ha='center', va='center')"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/setup.py b/setup.py
index 7c518ba0b..80ea122f7 100755
--- a/setup.py
+++ b/setup.py
@@ -48,8 +48,8 @@
####################################################################################################
-exec(compile(open('setup_data.py').read(), 'setup_data.py', 'exec'))
-
+#exec(compile(open('setup_data.py').read(), 'setup_data.py', 'exec'))
+from setup_data import setup_dict
####################################################################################################
setup_dict.update(dict(
@@ -83,6 +83,8 @@
'numpy',
'ply',
'scipy',
+ 'networkx',
+ 'SchemDraw'
],
))