Exemplo n.º 1
0
def main():

    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')

    parser.add_argument('--build',
                        default=False, action='store_true',
                        help='Build circuit')

    args = parser.parse_args()

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

    parser = SpiceParser(path=args.circuit_file)

    if args.build:
        parser.build_circuit()

    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)
Exemplo n.º 2
0
def main():

    # Monkeypatch the exec_command() to not bail if it detects an 'error' in stderr,
    # these 'errors' don't seem to actually prevent the sim from working correctly
    Shared.NgSpiceShared.exec_command = exec_command

    libraries_path = find_libraries()
    spice_library = SpiceLibrary(libraries_path)

    directory_path = Path(__file__).resolve().parent
    kicad_netlist_path = directory_path.joinpath(
        'wien-bridge-oscillator-sim.cir')
    parser = SpiceParser(path=str(kicad_netlist_path))
    circuit = parser.build_circuit(ground=0)
    simulator = circuit.simulator(temperature=25, nominal_temperature=25)
    analysis = simulator.transient(step_time=10 @ u_us, end_time=60 @ u_ms)

    v_sine_out = analysis['SINE_OUT']
    first_index_of_interest = np.where(
        v_sine_out.abscissa.as_ndarray() > 0.04)[0][0]

    fig, ax = plt.subplots(1, figsize=(10, 5))
    ax.plot(v_sine_out.abscissa.as_ndarray()[first_index_of_interest:],
            v_sine_out.as_ndarray()[first_index_of_interest:])
    ax.legend(('SINE_OUT', ), loc=(.8, .8))
    ax.grid()
    ax.set_xlabel('Time (s)')
    ax.set_ylabel('Voltage (V)')

    plt.tight_layout()
    plt.savefig('v-sine-out.png')
Exemplo n.º 3
0
def main():

    # Monkeypatch the exec_command() to not bail if it detects an 'error' in stderr,
    # these 'errors' don't seem to actually prevent the sim from working correctly
    Shared.NgSpiceShared.exec_command = exec_command

    libraries_path = find_libraries()
    spice_library = SpiceLibrary(libraries_path)

    directory_path = Path(__file__).resolve().parent
    kicad_netlist_path = directory_path.joinpath('simulation.cir')

    netlist_without_quotes_path = Path('./simulation-without-quotes.cir')
    remove_include_quotes(kicad_netlist_path, netlist_without_quotes_path)

    parser = SpiceParser(path=str(netlist_without_quotes_path))
    circuit = parser.build_circuit(ground=0)
    simulator = circuit.simulator(temperature=25, nominal_temperature=25)
    analysis = simulator.transient(step_time=100 @ u_ps, end_time=100 @ u_ns)

    v_out = analysis['OUT']

    fig, ax = plt.subplots(1, figsize=(10, 5))
    ax.plot(v_out.abscissa.as_ndarray() * 1e9, v_out.as_ndarray())
    ax.legend(('OUT', ), loc=(.8, .8))
    ax.grid()
    ax.set_xlabel('Time (ns)')
    ax.set_ylabel('Voltage (V)')

    plt.tight_layout()
    plt.savefig('out.png')
Exemplo n.º 4
0
 def parser_netlist(self):
     """
     解析netlist
     :return:self.circurt属性绑定解析的netlist电路
     """
     parser = SpiceParser(path=self._path)
     self.circuit = parser.build_circuit()
     print(str(self.circuit))
Exemplo n.º 5
0
def circuit_gft(prb):
    circuit_file = SpiceParser(source=prb[0])
    circuit = circuit_file.build_circuit()
    circuit.parameter('prb', str(prb[1]))
    # Fixme: simulate with Xyce, CI !!!
    simulator = circuit.simulator(simulator='xyce-serial')
    simulator.save(['all'])
    return simulator
Exemplo n.º 6
0
def is_valid_netlist(textfile, name=None):
    try:
        parser = SpiceParser(source=textfile)
        circuit = parser.build_circuit()
        return len(circuit.elements) > 0
    except:
        if name:
            print(f'invalid spice file: {name}', file=sys.stderr)
        return False
Exemplo n.º 7
0
def main():

    # Monkeypatch the exec_command() to not bail if it detects an 'error' in stderr,
    # these 'errors' don't seem to actually prevent the sim from working correctly
    Shared.NgSpiceShared.exec_command = exec_command

    libraries_path = find_libraries()
    spice_library = SpiceLibrary(libraries_path)

    directory_path = Path(__file__).resolve().parent
    kicad_netlist_path = directory_path.joinpath('schematic.cir')

    netlist_without_quotes_path = Path('./simulation-without-quotes.cir')
    remove_include_quotes(kicad_netlist_path, netlist_without_quotes_path)

    parser = SpiceParser(path=str(netlist_without_quotes_path))
    circuit = parser.build_circuit(ground=0)
    simulator = circuit.simulator(temperature=25, nominal_temperature=25)
    # analysis = simulator.transient(step_time=100@u_us, end_time=1000@u_ms)
    analysis = simulator.ac(start_frequency=100@u_mHz, stop_frequency=10@u_kHz, number_of_points=50,  variation='dec')

    v_out = analysis['v_out']
    mag = 20*np.log10(np.abs(v_out.as_ndarray()))
    print(mag)
    freq = analysis.frequency.as_ndarray()

    r1_Ohms = 2e3
    r2_Ohms = 10e3
    ratio = r2_Ohms / (r1_Ohms + r2_Ohms)
    ratio_dB = 20*np.log10(ratio)
    ratio_dB = -1.75
    r_parallel = (r1_Ohms * r2_Ohms) / (r1_Ohms + r2_Ohms)
    c_F = 10e-6
    fc = 1 / (2*np.pi*r_parallel*c_F)
    print(ratio_dB)

    fig, ax = plt.subplots(1, figsize=(10, 7))
    ax.plot(freq, mag, label='$V_{OUT}$')
    # ax.legend(('$V_{OUT}$',), loc=(.8,.8))
    ax.grid()
    ax.set_xscale('log')
    ax.set_xlabel('Frequency (Hz)')
    ax.set_ylabel('Voltage Magnitude (dB)')
    ax.axvline(fc, color='C2', label='$f_c$')
    ax.axhline(ratio_dB, color='C1', label=f'{ratio_dB:.2f}dB (DC gain)')
    ax.axhline(ratio_dB - 3.0, color='C1', label=f'{ratio_dB - 3:.2f}dB (DC gain - 3dB)')
    ax.legend()
    plt.tight_layout()
    plt.savefig('out.png')
Exemplo n.º 8
0
def fetch_graphs(files, min_edges=0):
    filenames = h.valid_netlist_sources(files)
    source = next(filenames)

    parser = SpiceParser(source=source)
    circuit = parser.build_circuit()
    (nodes, edges) = h.get_nodes_edges(circuit)

    # TODO: randomly remove one each time until it is empty
    graphs = []
    while len(edges) > min_edges:
        graphs.append((nodes, edges))
        (nodes, edges) = a.remove_random_node(nodes, edges)

    return graphs
Exemplo n.º 9
0
def build_cgraph_lib(ffn_netlist, dir_graph_out=None, ground=0, remove_dummy=False, dir_node_edge_list=None, dir_lib_out=None, 
                    guess_by_name=True, add_edge_annotation=False):
    """
        ffn_netlist: full file path of netlist to parse
        dir_graph_out: directory path to save the resulting circuit topology graph in yaml format
        dir_lib_out: directory path to save circuit topology graph lib in pickle format
        remove_dummy: when True, remove dummy mos
        guess_by_name: when True, some attributes such as signal type is inferred/guessed from net/instance name
        add_edge_annotation: when True, add matching/group edge constraints to the graph

        return dict (i.e. {circuit_name:networkx.Graph})
    """
    from PySpice.Spice.Parser import SpiceParser
    parser = SpiceParser(path=ffn_netlist)
    circuit = parser.build_circuit(ground=ground)
    
    cgraph_dict = {}
    top_name, ext = os.path.splitext(os.path.basename(ffn_netlist))
    G = build_cgraph(circuit, top_name, guess_by_name=guess_by_name, add_edge_annotation=add_edge_annotation)
    cgraph_dict[top_name] = G

    for c in (circuit.subcircuits):
        for e in (c.elements):
            G = build_cgraph(c, c.name, guess_by_name=guess_by_name, add_edge_annotation=add_edge_annotation)
        cgraph_dict[c.name] = G

    for name, G in cgraph_dict.items():
        if(dir_graph_out is not None):
            nx.write_yaml(G, os.path.join(dir_graph_out, "%s.yaml" % name))
        
        if(dir_node_edge_list is not None):
            node_number = {}
            with open(os.path.join(dir_node_edge_list, "%s.nodelist" % name), 'w') as f:
                for ii, n in enumerate(G.nodes()):
                    f.write("%s %d\n" % (n, ii))
                    node_number[n]=ii
            with open(os.path.join(dir_node_edge_list, "%s.edgelist" % name), 'w') as f:
                for e in G.edges():
                    f.write('%s %s\n' % (node_number[e[0]], node_number[e[1]]))

    if(dir_lib_out is not None):
        with open(os.path.join(dir_lib_out, "%s.cgraph.pkl" % top_name), 'wb') as f:
            pickle.dump(cgraph_dict, f, -1)
    return cgraph_dict
Exemplo n.º 10
0
def main():

    # Monkeypatch the exec_command() to not bail if it detects an 'error' in stderr,
    # these 'errors' don't seem to actually prevent the sim from working correctly
    Shared.NgSpiceShared.exec_command = exec_command

    libraries_path = find_libraries()
    spice_library = SpiceLibrary(libraries_path)

    directory_path = Path(__file__).resolve().parent
    kicad_netlist_path = directory_path.joinpath('schematic.cir')

    netlist_without_quotes_path = Path('./simulation-without-quotes.cir')
    remove_include_quotes(kicad_netlist_path, netlist_without_quotes_path)

    parser = SpiceParser(path=str(netlist_without_quotes_path))
    circuit = parser.build_circuit(ground=0)
    simulator = circuit.simulator(temperature=25, nominal_temperature=25)
    analysis = simulator.transient(step_time=1 @ u_us, end_time=1000 @ u_us)
    # analysis = simulator.ac(start_frequency=100@u_mHz, stop_frequency=10@u_kHz, number_of_points=50,  variation='dec')

    v_in = analysis['/v_in']
    v_out = analysis['v_out']

    fig, ax = plt.subplots(1, figsize=(7, 5))
    ax.plot(v_in.abscissa.as_ndarray() * 1e6,
            v_in.as_ndarray(),
            label='$v_{in}$')
    ax.plot(v_out.abscissa.as_ndarray() * 1e6,
            v_out.as_ndarray(),
            label='$v_{out}$')
    ax.axhline(-5.0, ls='--', color='C0')
    ax.axhline(5.0, ls='--', color='C0')
    ax.axhline(0, ls='--', color='C1')
    ax.axhline(3.3, ls='--', color='C1')
    ax.grid()
    ax.set_xlabel('Time [us]')
    ax.set_ylabel('Voltage [V]')
    ax.legend()
    plt.tight_layout()
    plt.savefig('out.png')
Exemplo n.º 11
0
def netlist_as_graph(textfile):
    parser = SpiceParser(source=textfile)
    circuit = parser.build_circuit()
    component_list = components(circuit)
    adj = {}

    for element in circuit.elements:
        element_id = component_list.index(element)

        if element_id not in adj:
            adj[element_id] = []

        nodes = [pin.node for pin in element.pins]
        node_ids = [component_list.index(node) for node in nodes]
        adj[element_id].extend(node_ids)

        for node_id in node_ids:
            if node_id not in adj:
                adj[node_id] = []
            adj[node_id].append(element_id)

    adj = nx.adjacency_matrix(nx.from_dict_of_lists(adj)).toarray()
    return component_list, adj
Exemplo n.º 12
0
class Circuit:
    parser = SpiceParser(source=".title" + os.linesep + ".end") # 1.28 ms -> 65 us
    def __init__(self, circuitTemplate, parameters):
        """A determined circuit with parameters all specified

        Attributes
        ----------

        circuitTemplate : CircuitTemplate object
            the template from which the circuit is instantiated
        parameters : list or 1-D ndarray
            numeric parameters that will be substituted for those undetermined parameters in the circuit template

        Properties
        ----------

        .circuitTemplate : CircuitTemplate object
            the template from which this circuit is instantiated
        .parameters : List
        """

        self.circuitTemplate = circuitTemplate
        self.parameters = parameters

        try:
            mapping = dict(zip(self.circuitTemplate.parameters, parameters))
            self._netlist = self.circuitTemplate.netlist.format(**mapping)
        except:
            traceback.print_exc()
            raise ValueError("insufficient number of parameters. Expect {} parameters: {}. Get {} parameters: {}".format(len(self.circuitTemplate.parameters), self.circuitTemplate.parameters, len(self.parameters), self.parameters))

        self._circuit = self.parser.build_circuit()
        self._circuit.raw_spice += self._netlist
        self._circuit.raw_spice += self.circuitTemplate.rawSpice
        self._simulator = self._circuit.simulator(simulator="ngspice-subprocess")

        self.hints = dict(
            ac = dict(
                start = 1,
                end = 1e+9,
                variation = "dec"
            ),
            tran = dict(
                start = 0,
                end = 1e-3,
                points = 100
            )
        )

        # self._cached = {}

    def getInput(self, nodeList):
        if "vin+" in nodeList:
            vin = nodeList["vin+"] - nodeList["vin-"]
        elif "vi+" in nodeList:
            vin = nodeList["vi+"] - nodeList["vi-"]
        elif "vin" in nodeList:
            vin = nodeList["vin"]
        elif "vi" in nodeList:
            vin = nodeList["vi"]
        elif "vp" in nodeList:
            vin = nodeList["vp"] - nodeList["vn"]
        else:
            raise KeyError("no input voltage node found. Tried `Vin+`, `vin+`, `Vi+`, `vi+`, `Vin`, `vin, `Vi`, `vi`, `Vp`, `vp`.")

        return np.array(vin) # remove units

    def getOutput(self, nodeList):
        if "vout+" in nodeList:
            vout = nodeList["vout+"] - nodeList["vout-"]
        elif "vo+" in nodeList:
            vout = nodeList["vo+"] - nodeList["vo-"]
        elif "vout" in nodeList:
            vout = nodeList["vout"]
        elif "vo" in nodeList:
            vout = nodeList["vo"]
        else:
            raise KeyError("no output voltage node found. Tried `Vout`, `vout`, `Vo`, `vo`.")

        return np.array(vout) # remove units

    def getResponse(self, nodeList):
        # Looks like PySpice will turn all node name into their lower case.
        vout = self.getOutput(nodeList)
        vin = self.getInput(nodeList)
        return vout / vin

    @property
    def netlist(self):
        return self._netlist

    # Methods for manual usage. They ignore `self.hints`.
    @functools.lru_cache()
    def getTransientModel(self, start=0, end=1e-6, points=1000):
        return self._simulator.transient(start_time=start, end_time=end, step_time=(end - start) / points)

    @functools.lru_cache()
    def getTransientResponse(self, start=0, end=1e-6, points=1000):
        analysis = self.getTransientModel(start, end, points)
        time = np.array(analysis.time)

        return (time, self.getResponse(analysis.nodes))

    @functools.lru_cache()
    def getSmallSignalModel(self, start=1, end=1e+9, points=10, variation="dec"):
        """Do an AC small-signal simulation

        Attributes
        ----------

        start : real number
            simulation frequency range start
        end : real number
            simulation frequency range end
        points : integer
            - when `variation == "lin"`, number of points in total
            - when `variation == "dec"`, number of points per decade
            - when `variation == "oct"`, number of points per octave
        variation : str
            sampling frequency point arrangement. Use "dec" or "oct" if you plan to draw Bode plot later.

        Returns
        -------

        analysis : PySpice analysis object
        """
        return self._simulator.ac(start_frequency=start, stop_frequency=end, number_of_points=points, variation=variation)

    @functools.lru_cache() # This boosts performance...
    def getFrequencyResponse(self, start=1, end=1e+9, points=10, variation="dec"):
        # analysis = self._simulator.ac(start_frequency=start, stop_frequency=end, number_of_points=points, variation=variation)
        analysis = self.getSmallSignalModel(start, end, points, variation)
        frequencies = np.array(analysis.frequency)

        return (frequencies, self.getResponse(analysis.nodes))

    # High-level, convenient property-styled methods. These are affected by `self.hints`

    @property
    @functools.lru_cache(maxsize=1)
    def operationalPoint(self):
        return self._simulator.operating_point()

    @property
    @functools.lru_cache(maxsize=1)
    def staticPower(self):
        """static power, aka. absolute value of supply voltage multiplies by absolute value of current through supply."""
        op = self.operationalPoint

        if "vdd+" in op.nodes:
            vdd = np.abs(op.nodes["vdd+"] - op.nodes["vdd-"])
        elif "vcc+" in op.nodes:
            vdd = np.abs(op.nodes["vcc+"] - op.nodes["vcc-"])
        elif "vdd" in op.nodes:
            vdd = np.abs(op.nodes["vdd"])
        elif "vcc" in op.nodes:
            vdd = np.abs(op.nodes["vcc"])
        else:
            raise KeyError("no supply found. Tried `VDD+`, `VDD`, `VCC+`, `VCC` and their case-insensitive variants.")
        
        if "vdd" in op.branches:
            idd = np.abs(op.branches["vdd"])
        elif "v0" in op.branches:
            idd = np.abs(op.branches["v0"])
        else:
            raise KeyError("no supply found. Tried `VDD`, `V0` and their case-insensitive variants.")

        return np.float(vdd) * np.float(idd)

    dcPower = staticPower

    @property
    def bandwidth(self):
        frequencyResponse = self.getFrequencyResponse(**self.hints["ac"])
        return sizer.calculators.bandwidth(frequencyResponse[0], frequencyResponse[1])

    @property
    def phaseMargin(self):
        frequencyResponse = self.getFrequencyResponse(**self.hints["ac"])
        return sizer.calculators.phaseMargin(frequencyResponse[0], frequencyResponse[1])

    @property
    def gainMargin(self):
        frequencyResponse = self.getFrequencyResponse(**self.hints["ac"])
        return sizer.calculators.gainMargin(frequencyResponse[0], frequencyResponse[1])

    @property
    def unityGainFrequency(self):
        frequencyResponse = self.getFrequencyResponse(**self.hints["ac"])
        return sizer.calculators.unityGainFrequency(frequencyResponse[0], frequencyResponse[1])

    @property
    def gain(self):
        frequencyResponse = self.getFrequencyResponse(**self.hints["ac"])
        return sizer.calculators.gain(frequencyResponse[0], frequencyResponse[1])

    @property
    def slewRate(self):
        analysis = self.getTransientModel(**self.hints["tran"])
        return sizer.calculators.slewRate(np.array(analysis.time), np.array(self.getOutput(analysis.nodes)))
Exemplo n.º 13
0
    ##############################################

    def __init__(self):

        super().__init__()

        self.R('load', 'output', 'x', 10 @ u_Ω)


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

#!# We read the generated netlist.
kicad_netlist_path = os.path.join(os.path.realpath(os.path.dirname(__file__)),
                                  'kicad-pyspice-example',
                                  'kicad-pyspice-example.cir')
parser = SpiceParser(path=kicad_netlist_path)

#!# We build the circuit and translate the ground (5 to 0).
circuit = parser.build_circuit(ground=5)

#!# We include the operational amplifier module.
circuit.include(spice_library['LMV981'])

#!# We define the subcircuits.
for subcircuit in (PowerIn(), Opamp(), JackIn(), JackOut()):
    circuit.subcircuit(subcircuit)

# print(str(circuit))

#!# We perform a transient simulation.
simulator = circuit.simulator(temperature=25, nominal_temperature=25)
Exemplo n.º 14
0
    ##############################################

    def __init__(self):

        super().__init__()

        self.R('load', 'output', 'x', 10 @ u_Ω)


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

#r# We read the generated netlist.
directory_path = Path(__file__).resolve().parent
kicad_netlist_path = directory_path.joinpath('kicad-pyspice-example',
                                             'kicad-pyspice-example.cir')
parser = SpiceParser(path=str(kicad_netlist_path))

#r# We build the circuit and translate the ground (5 to 0).
circuit = parser.build_circuit(ground=5)

#r# We include the operational amplifier module.
circuit.include(spice_library['LMV981'])

#r# We define the subcircuits.
for subcircuit in (PowerIn(), Opamp(), JackIn(), JackOut()):
    circuit.subcircuit(subcircuit)

# print(str(circuit))

#r# We perform a transient simulation.
simulator = circuit.simulator(temperature=25, nominal_temperature=25)
Exemplo n.º 15
0
#
#.lib CMOS_035_Spice_Model.lib tt
#
#.end
# """

source = """
.title Test
V1 vp GND dc 1.65 ac 0.5
M7 Vout /6 VDD VDD p_33 l=0.9u w=25.9u
M6 Vout /3 GND GND n_33 l=1.4u w=92.04u
M2 /3 vp /1 VDD p_33 l=0.9u w=51.78u
M1 /2 vn /1 VDD p_33 l=0.9u w=51.78u
M4 /3 /2 GND GND n_33 l=1.4u w=46.02u
M3 /2 /2 GND GND n_33 l=1.4u w=46.02u
M5 /1 /6 VDD VDD p_33 l=0.9u w=12.95u
V0 VDD GND 3.3
M8 /6 /6 VDD VDD p_33 l=0.9u w=1.3u
I1 /6 GND 10u
.lib CMOS_035_Spice_Model.lib tt
""".strip()

parser = SpiceParser(source=source)
circuit = parser.build_circuit()
source2 = str(circuit)
for line1, line2 in zip(source.splitlines(), source2.splitlines()):
    print('-' * 100)
    print(line1 + '|')
    print(line2 + '|')
    assert (line1 == line2)
Exemplo n.º 16
0
from PySpice.Doc.ExampleTools import find_libraries
from PySpice.Probe.Plot import plot
from PySpice.Spice.Library import SpiceLibrary
from PySpice.Spice.Netlist import Circuit
from PySpice.Spice.Parser import SpiceParser
from PySpice.Spice.Netlist import SubCircuitFactory
from PySpice.Unit import *


####################################################################################################
# IMPORT SPICE NETLIST OF CIRCUIT:

spice_library = SpiceLibrary('../libraries/')
netlist_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'NAND2.sp')
parser = SpiceParser(path=netlist_path)
circuit = parser.build_circuit()
circuit.include(spice_library['NAND2'])


####################################################################################################
# CONFIGURE VOLTAGE SOURCES

supply = 1@u_V
circuit.V('supply', 'vdd', 'vss', supply)
circuit.V('ground', 'vss', circuit.gnd, circuit.gnd)


####################################################################################################
# SIMULATE IMPORTED CIRCUIT