Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions PySpice/Spice/BasicElement.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -116,6 +117,8 @@
ModelPositionalParameter,
)

import SchemDraw as schem

####################################################################################################

class DipoleElement(FixedPinElement):
Expand Down Expand Up @@ -153,19 +156,26 @@ 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():
# parameter = KeyValueParameter(key)
# 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)

##############################################

Expand Down Expand Up @@ -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_Ω)
Expand Down Expand Up @@ -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)
Expand Down
118 changes: 87 additions & 31 deletions PySpice/Spice/Netlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ def __init__(self, **kwargs):
import logging
import os

# import networkx
import networkx
import SchemDraw
import numpy as np

####################################################################################################

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -428,59 +430,63 @@ 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:
prefix = namespace['__prefix__']
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]

##############################################

# Note: These properties are only available from the class object
# 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__

####################################################################################################

Expand All @@ -501,6 +507,8 @@ class Element(metaclass=ElementParameterMetaClass):

#: SPICE element prefix
__prefix__ = None

schematic = None

##############################################

Expand All @@ -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):
Expand All @@ -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)

##############################################

Expand Down Expand Up @@ -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)

##############################################

Expand All @@ -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):
Expand Down Expand Up @@ -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
Expand All @@ -834,7 +847,6 @@ def __init__(self):

self.raw_spice = ''

# self._graph = networkx.Graph()

##############################################

Expand Down Expand Up @@ -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

##############################################

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -1138,6 +1193,7 @@ class SubCircuitFactory(SubCircuit):

__name__ = None
__nodes__ = None
__pins__ = None

##############################################

Expand Down
1 change: 1 addition & 0 deletions PySpice/Spice/NgSpice/SimulationType.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@
)

SIMULATION_TYPE[28] = SIMULATION_TYPE[27]
SIMULATION_TYPE[29] = SIMULATION_TYPE[28]
Loading