nunobrum / PyLTSpice

Set of tools to interact with LTSpice. See README file for more information.
https://www.nunobrum.com/pyltspice.html
GNU General Public License v3.0
203 stars 59 forks source link
electronics simulation spice

README

PyLTSpice is a toolchain of python utilities design to interact with LTSpice Electronic Simulator. It is mostly based on the spicelib package, being the main difference to it is the fact that LTSpice is automatically selected to perform all simulations.

What is contained in this repository

How to Install

pip install PyLTSpice

Updating PyLTSpice

pip install --upgrade PyLTSpice

Using GITHub

git clone https://github.com/nunobrum/PyLTSpice.git

If using this method it would be good to add the path where you cloned the site to python path.

import sys
sys.path.append(<path to PyLTSpice>)

How to use

Here follows a quick outlook on how to use each of the tools.

More comprehensive documentation can be found in https://pyltspice.readthedocs.io/en/latest/

LICENSE

GNU V3 License (refer to the LICENSE file)

RawRead

The example below reads the data from a Spice Simulation called "TRAN - STEP.raw" and displays all steps of the "I(R1)" trace in a matplotlib plot

from PyLTSpice import RawRead

from matplotlib import pyplot as plt

LTR = RawRead("./testfiles/TRAN - STEP.raw")

print(LTR.get_trace_names())
print(LTR.get_raw_property())

IR1 = LTR.get_trace("I(R1)")
x = LTR.get_trace('time')  # Gets the time axis
steps = LTR.get_steps()
for step in range(len(steps)):
    # print(steps[step])
    plt.plot(x.get_wave(step), IR1.get_wave(step), label=steps[step])

plt.legend()  # order a legend
plt.show()

-- in examples/raw_read_example.py

RawWrite

The following example writes a RAW file with a 3 milliseconds transient simulation sine with a 10kHz and a cosine with 9.997kHz

import numpy as np
from PyLTSpice import RawRead, Trace, RawWrite
LW = RawWrite(fastacces=False)
tx = Trace('time', np.arange(0.0, 3e-3, 997E-11))
vy = Trace('N001', np.sin(2 * np.pi * tx.data * 10000))
vz = Trace('N002', np.cos(2 * np.pi * tx.data * 9970))
LW.add_trace(tx)
LW.add_trace(vy)
LW.add_trace(vz)
LW.save("./testfiles/teste_snippet1.raw")

-- in examples/raw_write_example.py [RawWrite Example]

SpiceEditor, AscEditor and SimRunner.py

This module is used to launch LTSPice simulations. Results then can be processed with either the RawRead or with the LTSteps module to read the log file which can contain .MEAS results.

The script will firstly invoke the LTSpice in command line to generate a netlist, and then this netlist can be updated directly by the script, in order to change component values, parameters or simulation commands.

Here follows an example of operation.

from PyLTSpice import SimRunner
from PyLTSpice import SpiceEditor

# Force another simulatior
simulator = r"C:\Program Files\LTC\LTspiceXVII\XVIIx64.exe"

# select spice model
LTC = SimRunner(output_folder='./temp')
LTC.create_netlist('./testfiles/Batch_Test.asc')
netlist = SpiceEditor('./testfiles/Batch_Test.net')
# set default arguments
netlist.set_parameters(res=0, cap=100e-6)
netlist.set_component_value('R2', '2k')  # Modifying the value of a resistor
netlist.set_component_value('R1', '4k')
netlist.set_element_model('V3', "SINE(0 1 3k 0 0 0)")  # Modifying the
netlist.set_component_value('XU1:C2', 20e-12)  # modifying a define simulation
netlist.add_instructions(
    "; Simulation settings",
    ";.param run = 0"
)
netlist.set_parameter('run', 0)

for opamp in ('AD712', 'AD820'):
    netlist.set_element_model('XU1', opamp)
    for supply_voltage in (5, 10, 15):
        netlist.set_component_value('V1', supply_voltage)
        netlist.set_component_value('V2', -supply_voltage)
        print("simulating OpAmp", opamp, "Voltage", supply_voltage)
        LTC.run(netlist)

for raw, log in LTC:
    print("Raw file: %s, Log file: %s" % (raw, log))
    # do something with the data
    # raw_data = RawRead(raw)
    # log_data = LTSteps(log)
    # ...

netlist.reset_netlist()
netlist.add_instructions(
    "; Simulation settings",
    ".ac dec 30 10 1Meg",
    ".meas AC Gain MAX mag(V(out)) ; find the peak response and call it ""Gain""",
    ".meas AC Fcut TRIG mag(V(out))=Gain/sqrt(2) FALL=last"
)

# Sim Statistics
print('Successful/Total Simulations: ' + str(LTC.okSim) + '/' + str(LTC.runno))

enter = input("Press enter to delete created files")
if enter == '':
    LTC.file_cleanup()

-- in examples/sim_runner_example.py

The example above is using the SpiceEditor to create and modify a spice netlist, but it is also possible to use the AscEditor to directly modify the .asc file. The edited .asc file can then be opened by the LTSpice GUI and the simulation can be run from there.

Simulation Analysis Toolkit

The AscEditor can be used with the Simulation Analysis Toolkit to perform Monte Carlo or Wost Case simulations. These simulations can either be done on the LTSpice GUI or using the Runner Class described above.

Let's consider the following circuit:

Sallen-Key Amplifier

When performing a Monte Carlo simulation on this circuit, we need to manually modify the value of each component, and then add the .step command for making several runs on the same circuit. To simplify this process, the AscEditor class can be used as exemplified below:

from PyLTSpice import AscEditor, SimRunner  # Imports the class that manipulates the asc file
from PyLTSpice.sim.tookit.montecarlo import Montecarlo  # Imports the Montecarlo toolkit class

sallenkey = AscEditor("./testfiles/sallenkey.asc")  # Reads the asc file into memory
runner = SimRunner(output_folder='./temp_mc')  # Instantiates the runner class, with the output folder already set
mc = Montecarlo(sallenkey, runner)  # Instantiates the Montecarlo class, with the asc file already in memory

# The following lines set the default tolerances for the components
mc.set_tolerance('R', 0.01)  # 1% tolerance, default distribution is uniform
mc.set_tolerance('C', 0.1, distribution='uniform')  # 10% tolerance, explicit uniform distribution
mc.set_tolerance('V', 0.1, distribution='normal')  # 10% tolerance, but using a normal distribution

# Some components can have a different tolerance
mc.set_tolerance('R1', 0.05)  # 5% tolerance for R1 only. This only overrides the default tolerance for R1

# Tolerances can be set for parameters as well
mc.set_parameter_deviation('Vos', 3e-4, 5e-3, 'uniform')  # The keyword 'distribution' is optional
mc.prepare_testbench(num_runs=1000)  # Prepares the testbench for 1000 simulations

# Finally the netlist is saved to a file
mc.save_netlist('./testfiles/sallenkey_mc.net')

mc.run_testbench(runs_per_sim=100)  # Runs the simulation with splits of 100 runs each
logs = mc.read_logfiles()   # Reads the log files and stores the results in the results attribute
logs.obtain_amplitude_and_phase_from_complex_values()  # Splits the complex values into real and imaginary parts
logs.export_data('./temp_mc/data_testbench.csv')  # Exports the data to a csv file
logs.plot_histogram('fcut')  # Plots the histograms for the results
mc.cleanup_files()  # Deletes the temporary files

print("=====================================")
# Now using the second method, where the simulations are ran one by one
mc.clear_simulation_data()  # Clears the simulation data
mc.reset_netlist()  # Resets the netlist to the original
mc.run_analysis(num_runs=1000)  # Runs the 1000 simulations
logs = mc.read_logfiles()   # Reads the log files and stores the results in the results attribute
logs.export_data('./temp_mc/data_sims.csv')  # Exports the data to a csv file
logs.plot_histogram('fcut')  # Plots the histograms for the results
mc.cleanup_files()  # Deletes the temporary files

-- in examples/run_montecarlo.py

When opening the created sallenkey_mc.net file, we can see that the following circuit.

Sallen-Key Amplifier with Montecarlo

The following updates were made to the circuit:

Similarly, the worst case analysis can also be setup by using the class WorstCaseAnalysis, as exemplified below:

from PyLTSpice import AscEditor, SimRunner  # Imports the class that manipulates the asc file
from PyLTSpice.sim.tookit.worst_case import WorstCaseAnalysis

sallenkey = AscEditor("./testfiles/sallenkey.asc")  # Reads the asc file into memory
runner = SimRunner(output_folder='./temp_wca')  # Instantiates the runner class, with the output folder already set
wca = WorstCaseAnalysis(sallenkey, runner)  # Instantiates the Worst Case Analysis class

# The following lines set the default tolerances for the components
wca.set_tolerance('R', 0.01)  # 1% tolerance
wca.set_tolerance('C', 0.1)  # 10% tolerance
wca.set_tolerance('V', 0.1)  # 10% tolerance. For Worst Case analysis, the distribution is irrelevant

# Some components can have a different tolerance
wca.set_tolerance('R1', 0.05)  # 5% tolerance for R1 only. This only overrides the default tolerance for R1

# Tolerances can be set for parameters as well.
wca.set_parameter_deviation('Vos', 3e-4, 5e-3)

# Finally the netlist is saved to a file
wca.save_netlist('./testfiles/sallenkey_wc.asc')

wca.run_testbench()  # Runs the simulation with splits of 100 runs each

logs = wca.read_logfiles()   # Reads the log files and stores the results in the results attribute
logs.export_data('./temp_wca/data.csv')  # Exports the data to a csv file

print("Worst case results:")
for param in ('fcut', 'fcut_FROM'):
    print(f"{param}: min:{logs.min_measure_value(param)} max:{logs.max_measure_value(param)}")

wca.cleanup_files()  # Deletes the temporary files

-- in examples/run_worst_case.py

When opening the created sallenkey_wc.net file, we can see that the following circuit.

Sallen-Key Amplifier with WCA

The following updates were made to the circuit:

LTSteps.py

This module defines a class that can be used to parse LTSpice log files where the information about .STEP information is written. There are two possible usages of this module, either programmatically by importing the module and then accessing data through the class as exemplified here:

#!/usr/bin/env python
# coding=utf-8

from PyLTSpice.log.ltsteps import LTSpiceLogReader

data = LTSpiceLogReader("./testfiles/Batch_Test_AD820_15.log")

print("Number of steps  :", data.step_count)
step_names = data.get_step_vars()
meas_names = data.get_measure_names()

# Printing Headers
print(' '.join([f"{step:15s}" for step in step_names]), end='')  # Print steps names with no new line
print(' '.join([f"{name:15s}" for name in meas_names]), end='\n')
# Printing data
for i in range(data.step_count):
    print(' '.join([f"{data[step][i]:15}" for step in step_names]), end='')  # Print steps names with no new line
    print(' '.join([f"{data[name][i]:15}" for name in meas_names]), end='\n')  # Print Header

print("Total number of measures found :", data.measure_count)

-- in examples/ltsteps_example.py

The second possibility is to use the module directly on the command line

Command Line Interface

ltsteps.exe

The can be either be a log file (.log), a data export file (.txt) or a measurement output file (.meas) This will process all the data and export it automatically into a text file with the extension (tlog, tsv, tmeas) where the data read is formatted into a more convenient tab separated format. In case the is not provided, the script will scan the directory and process the newest log, txt or out file found.

histogram.exe

This module uses the data inside on the filename to produce a histogram image.

Usage: Histogram.py [options] LOG_FILE TRACE

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -s SIGMA, --sigma=SIGMA
                        Sigma to be used in the distribution fit. Default=3
  -n NBINS, --nbins=NBINS
                        Number of bins to be used in the histogram. Default=20
  -c FILTERS, --condition=FILTERS
                        Filter condition writen in python. More than one
                        expression can be added but each expression should be
                        preceded by -c. EXAMPLE: -c V(N001)>4 -c parameter==1
                        -c  I(V1)<0.5
  -f FORMAT, --format=FORMAT
                        Format string for the X axis. Example: -f %3.4f
  -t TITLE, --title=TITLE
                        Title to appear on the top of the histogram.
  -r RANGE, --range=RANGE
                        Range of the X axis to use for the histogram in the
                        form min:max. Example: -r -1:1
  -C, --clipboard       If the data from the clipboard is to be used.
  -i IMAGEFILE, --image=IMAGEFILE
                        Name of the image File. extension 'png'    

rawconvert.exe

A tool to convert .raw files into csv or Excel files.

Usage: raw_convert.exe [options] <rawfile> <trace_list>

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -o FILE, --output=FILE
                        Output file name. Use .csv for CSV output, .xlsx for
                        Excel output
  -c, --clipboard       Output to clipboard
  -v, --verbose         Verbose output
  -s SEPARATOR, --sep=SEPARATOR
                        Value separator for CSV output. Default: "\t" <TAB>
                        Example: -d ";"

run_server.exe

This command line tool was moved to the spicelib package.

SemiDevOpReader.py

This module is used to read from LTSpice log files Semiconductor Devices Operating Point Information. A more detailed documentation is directly included in the source file docstrings.

Debug Logging

The library uses the standard logging module. Three convenience functions have been added for easily changing logging settings across the entire library. PyLTSpice.all_loggers() returns a list of all the logger's names, PyLTSpice.set_log_level(logging.DEBUG) would set the library's logging level to debug, and PyLTSpice.add_log_handler(my_handler) would add my_handler as a handler for all loggers.

Single Module Logging

It is also possible to set the logging settings for a single module by using its name acquired from the PyLTSpice.all_loggers() function. For example:

import logging

logging.basicConfig(level=logging.INFO)  # Set up the root logger first

import PyLTSpice  # Import PyLTSpice to set the logging levels

PyLTSpice.set_log_level(logging.DEBUG)  # Set PyLTSpice's global log level
logging.getLogger("PyLTSpice.RawRead").level = logging.WARNING  # Set the log level for only RawRead to warning

Would set only PyLTSpice.RawRead file's logging level to warning while the other modules would remain at debug level. Make sure to initialize the root logger before importing the library to be able to see the logs.

To whom do I talk to?

For support and improvement requests please open an Issue in GitHub spicelib issues

History