Giter Site home page Giter Site logo

nunobrum / pyltspice Goto Github PK

View Code? Open in Web Editor NEW
193.0 12.0 59.0 2.02 MB

Set of tools to interact with LTSpice. See README file for more information.

Home Page: https://www.nunobrum.com/pyltspice.html

License: GNU General Public License v3.0

Python 92.49% Makefile 0.77% Batchfile 1.00% AGS Script 5.74%
electronics simulation spice

pyltspice's Introduction

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

  • LTSteps.py An utility that extracts from LTSpice output files data, and formats it for import in a spreadsheet, such like Excel or Calc.

  • raw_read.py A pure python class that serves to read raw files into a python class.

  • raw_write.py A class to write RAW files that can be read by LTSpice Wave Application.

  • spice_editor.py and asc_editor.py Scripts that can update spice netlists. The following methods are available to manipulate the component values, parameters as well as the simulation commands. These methods allow to update a netlist without having to open the schematic in LTSpice. The simulations can then be run in batch mode (see sim_runner.py).

    • set_element_model('D1', '1N4148') # Replaces the Diode D1 with the model 1N4148
    • set_component_value('R2', '33k') # Replaces the value of R2 by 33k
    • set_parameters(run=1, TEMP=80) # Creates or updates the netlist to have .PARAM run=1 or .PARAM TEMP=80
    • add_instructions(".STEP run -1 1023 1", ".dc V1 -5 5")
    • remove_instruction(".STEP run -1 1023 1") # Removes previously added instruction
    • reset_netlist() # Resets all edits done to the netlist.
  • sim_runner.py A python script that can be used to run LTSpice simulations in batch mode without having to open the LTSpice GUI. This in cooperation with the classes defined in spice_editor.py or asc_editor.py is useful because:

    • Can overcome the limitation of only stepping 3 parameters
    • Different types of simulations .TRAN .AC .NOISE can be run in a single batch
    • The RAW Files are smaller and easier to treat
    • When used with the RawRead.py and LTSteps.py, validation of the circuit can be done automatically.
    • Different models can be simulated in a single batch, by using the following instructions:

    Note: It was only tested with Windows based installations.

  • Analysis Toolkit A set of tools that prepare an LTSpice netlist for a Montecarlo or Worst Case Analysis. The device tolerances are set by the user and the netlist is updated accordingly. The netlist can then be used with the sim_runner.py to run a batch of simulations or with the LTSpice GUI.

  • Histogram.py A python script that uses numpy and matplotlib to create a histogram and calculate the sigma deviations. This is useful for Monte-Carlo analysis.

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:

  • The value of each component was replaced by a function that generates a random value within the specified tolerance.
  • The .step param run command was added to the netlist. Starts at -1 which it's the nominal value simulation, and finishes that the number of simulations specified in the prepare_testbench() method.
  • A default value for the run parameter was added. This is useful if the .step param run is commented out.
  • The R1 tolerance is different from the other resistors. This is because the tolerance was explicitly set for R1.
  • The Vos parameter was added to the .param list. This is because the parameter was explicitly set using the set_parameter_deviation method.
  • Functions utol, ntol and urng were added to the .func list. These functions are used to generate random values. Uniform distributions use the LTSpice built-in mc(x, tol) and flat(x) functions, while normal distributions use the gauss(x) function.

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:

  • The value of each component was replaced by a function that generates a nominal, minimum and maximum value depending on the run parameter and is assigned a unique index number. (R1=0, Vos=1, R2=2, ... V2=7, VIN=8) The unique number corresponds to the bit position of the run parameter. Bit 0 corresponds to the minimum value and bit 1 corresponds to the maximum value. Calculating all possible permutations of maximum and minimum values for each component, we get 2**9 = 512 possible combinations. This maps into a 9 bit binary number, which is the run parameter.
  • The .step param run command was added to the netlist. It starts at -1 which it's the nominal value simulation, then 0 which corresponds to the minimum value for each component, then it makes all combinations of minimum and maximum values until 511, which is the simulation with all maximum values.
  • A default value for the run parameter was added. This is useful if the .step param run is commented out.
  • The R1 tolerance is different from the other resistors. This is because the tolerance was explicitly set for R1.
  • The wc() function is added to the circuit. This function is used to calculate the worst case value for each component, given a tolerance value and its respective index.
  • The wc1() function is added to the circuit. This function is used to calculate the worst case value for each component, given a minimum and maximum value and its respective index.

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?

History

  • Version 5.3.1

    • Bugfixes
      • Updates on the README.md file. Issue #137
      • Hierarchical support on AscEditor. Issue #138
    • Rich format is only used if the user has it already installed. Issue #136
  • Version 5.3.0

    • Hierarchical Support (Aligning with the spicelib 1.1.1)
  • Version 5.2.3

    • Updating loggers to use the "spicelib" IDs.
    • Fixing autodoc errors
    • Correcting Version references
  • Version 5.2.2

    • Fixes on the unittests after the spicelib update to 1.0.4
  • Version 5.2.1

    • Correction on the run_montecarlo.py and run_worst_case.py examples.
    • Fixes on the spicelib (Version 1.0.3)
  • Version 5.2

    • SimAnalysis supporting both Qspice and LTSpice logfiles.
    • FastWorstCaseAnalysis algorithm implemented
    • Fix on the log reading of fourier data.
  • Version 5.1

    • Important Bugfix on the LTComplex class.
    • Fixes and enhancing the analysis toolkit.
    • Deprecating SpiceEditor.write_netlist in favour of save_netlist()
  • Version 5.0

    • Making this library dependent on spicelib while trying to maintain backward compatibility as much as possible. PyLTspice will be kept alive and its update will be linked to the spicelib. The main difference is that using PyLTspice will avert the need of having to select a simulator in all run commands.
  • Version 4.1.2

    • Adding support for the new QSPICE simulator
    • Improving the timeout mechanism on the SimRunner class
    • Minor bug fixes
  • Version 4.1.1

    • Completing the Worst-Case Analysis functions. Adding a dedicated example for it.
    • Refactoring the LTSpiceLogReader class in order to use it on the Analysis toolkit
  • Version 4.1.0 (requires Python 3.8 or higher)

    • Adding a new class to manipulate directly the .asc files.
    • Modifying all the other classes in order to use the new class.
    • Adding classes to perform Montecarlo and worst case analysis (Thanks to @mvanriet for his starting this).
    • Removing the deprecated LTSpice_RawRead.py, LTSpice_RawWrite.py and LTSpiceBatch.py files and respective classes.
    • Restructured the folder structure to be more in line with the Python standards.
    • Added an Examples folder with some examples on how to use the library.
  • Version 4.0.6

    • Fixing issue with the write_netlist() function when receiving a string instead of a pathlib.Path object.
    • Changing the regular expression for the resistor in order to accept the R= prefix on the values.
  • Version 4.0.5

    • Accepting fixes from aanas-sayed@GitHub that fixes issues with running the LTSpice in Linux.
  • Version 4.0.4

    • Improved usage of the logging library. (Thanks @TSprech for vastly improving the logging)
    • Included RunTask number in the log messages.
    • Included milliseconds in the time elapsed calculation.
  • Version 4.0.3

    • Fixing issue in elapsed time calculation.
    • Fixing issue with the import of LTSpiceLogReader from LTSteps.py
  • Version 4.0.2

    • Changing list of Library dependencies.
  • Version 4.0.1

    • Bug fix on CLI for the Histogram.py and LTSteps.py
  • Version 4.0.0

    • Separating the SimCommander into two separate classes, one for the spice netlist editing (SpiceEditor) and another for the simulation execution (SimRunner).
    • Implementing simulation server to allow for remote simulation execution and the respective client.
    • Supporting Wiggler element in the new LTSpiceXVII.
    • Renaming all files into lowercase.
    • Creating Error classes for better error handling.
    • Adding support for other simulators (ex: ngspice) where the simulator is defined by a class. This support class needs to be a subclass of the abstract class Simulator.
    • Enormous improvement in the documentation of the code.
  • Version 3.0

    • Eliminating the LTSpice prefixes from files and classes.
    • Adopting the lowercase convention for filenames.
  • Version 2.3.1

    • Bug fix on the parameter replacement.
  • Version 2.3

    • Supporting the creation of RAW Noise Analysis
    • Bug Fixes (See GitHub Log)
  • Version 2.2

    • Making numpy as a requirement and eliminating all code that avoided the use of numpy
    • Using new packaging tool
    • Fixes on the LTSpice_RawWrite
    • Fixes in the handling of stepped operating point simulations
  • Version 2.1

    • Adopting minimum python version 3.7
    • Starting to use unit tests to validate all modules and improving testbenches
    • Compatibility with NGSpice
    • Avoiding the use of setup.py as per PEP517 and PEP518
    • Bug Fixes (See GitHub log for more information)
    • Improvements on the management of stepped data in the LTSpice_RawRead.py
  • Version 2.0.2

    • Improvements on Encoding detection
  • Version 2.0

    • International Support using the correct encoding when loading log files.
    • Code Optimizations on the LTSpice_RawReader that allow faster data loading.
    • Improving the functionality on the LTSpice_RawWriter.py
    • Adding support to editing components inside subcircuits (.subckt)
    • Supporting resistors with Model Definitions
    • Fixing problem with LTSpiceLogReader that would return messed up data
    • Fixing problem with replacing the file extension in certain names
    • Correcting problem with deprecations on the numpy functions used by the Histogram.py
    • Adding back the README.md that somehow was deleted
  • Version 1.9

    • Adding support for µ character in the SpiceEditor.
    • Adding get_component_floatvalue() method in the netlist manipulating class that handles the conversion of numeric fields into a float. This function takes into account the engineering qualifiers 'k' for kilos, 'm' or milis, 'u' or 'µ' for microns, 'n' for nanos, 'f' for femtos and 'Meg' for Megas.
  • Version 1.8

    • Uniforming License reference across files and improvements on the documentation
    • An enormous and wholehearted thanks to @lpherr for the improvements in the documentation.
    • Bugfix on the add_LTspiceRunCmdLineSwitches() ; Supporting .param name value format
    • Allowing the LTSpiceRawRead to proceed when the log file can't be found or when there are problems reading it.
  • Version 1.7

    • Running in Linux under wine is now possible
  • Version 1.6

    • Adding LTSpice_RawWrite. Adding documentation.
  • Version 1.5

    • Small fixes and improvements on the class usage. No added features
  • Version 1.4

    • Adding the LTSpice_SemiDevOpReader module
    • Re-enabling the Histogram functions which where disabled by mistake.
  • Version 1.3

    • Bug fixes on the SpiceEditor Class
  • Version 1.2

    • README.md: Adding link to readthedocs documentation
    • All files: Comprehensive documentation on how to use each module
  • Version 1.1

    • README.md: Updated the description
    • LTSpiceBatch.py: Corrected the name of the returned raw file.
    • Added comments throughout the code and cleanup
  • Version 1.0

    • LTSpiceBatch.py:
      Implemented a new approach (NOT BACKWARDS COMPATIBLE), that avoids the usage of the sim_settings.inc file. And allows to modify not only parameters, but also models and even the simulation commands.
    • LTSpice_RawRead.py:
      Added the get_time_axis method to the RawRead class to avoid the problems with negative values on time axis, when 2nd order compression is enabled in LTSpice.
    • LTSteps.py:
      Modified the LTSteps, so it can also read measurements on log files without any steps done.
  • Version 0.6

    • Histogram.py now has an option to make the histogram directly from values stored in the clipboard
  • Version 0.5

    • The LTSpice_RawReader.py now uses the struct.unpack function for a faster execution
  • Version 0.4

    • Added LTSpiceBatch.py to the collection of tools
  • Version 0.3

    • A version of LTSteps that can be imported to use in a higher level script
  • Version 0.2

    • Adding LTSteps.py and Histogram.py
  • Version 0.1

    • First commit to the bitbucket repository.

pyltspice's People

Contributors

akaeba avatar ampxtreme avatar asgeroverby avatar jaccharrison avatar jakeyap avatar kgoodrick-rl avatar l-johnston avatar lenty avatar lpherr avatar mvanriet avatar nikolarobottesla avatar nunobrum avatar sudden6 avatar tpfau avatar tsprech avatar wardgssens avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyltspice's Issues

SemiDevOpReader not functional on MacOS

Heya, thanks for creating this awesome tool! I'm having some trouble with the SemiDevOpReader on MacOS, which I believe is due to no encoding being selected.

The issue

An LTSpice simulation generated the log file Mos.log on MacOS. Running PyLTSpice.LTSpice_SemiDevOpReader.opLogReader("Mos.log") returns an empty dictionary {} rather than the expected device parameters.

Suggested fix

Comparing the implementation of opLogReader with LTSpiceLogReader, the encoding detection is missing. This would cause all sorts of mayhem in parsing the file.

Hope this helps!

RawRead doesn't currently support complex data such as from AC analysis.

Issue

The existing LTSpiceRawRead only supports analysis types, such as TRAN, that produce real data. AC analysis produces complex data and is a significant use case.

Potential solution - l-johnston@b32ceb9f53bebed3fcf372d43ef900dad090a2e7

I propose extending LTSpiceRawRead to support complex data. I show one way to do this in the referenced branch. I added the class RawRead that adds support for complex while keeping the original data structures, methods and API.

Request

Review the proposed solution and provide feedback.

_get_param_line stops at first match

Hi,

It seems that in a netlist with nested params definitions, the set_parameter may not be able to find the right line to modify because the _get_param_line will stop as soon as a string match is found regardless of on wich side of the equal sign the param is.

E.g. in the following netlist :

I1 0 N007 SINE(0 {I1p} 1k 0 0 0)

.param I1p=I1rms*sqrt(2)
.param I1rms=100

BR,
Jak

README description does not suit V1.0 - update proposal

Dear Nuno,

For version 1.0, you decided to move away from including a sim_settings.lib/.inc file. However, the README file still contains this as a description in the paragraph "What is contained in this repository". Moving this particular description to a dedicated article describing "earlier versions" may be a useful clearification.
Also, it might be interesting to shortly summarize what needs to be done to move compatibility to the newest version of PyLTSpice. My suggestion:
1.) Remove/comment out ".inc sim_settings.lib" in the LTSpice schematic file
2.) Substitute any "LTC.set_settings(...)" call (with LTC being an instance of LTSpiceBatch.LTCommander) by "LTC.add_instructions(...)"
3.) Keep in mind that the .raw filename has change by the suffix "_run" for later processing steps.
Best regards and many thanks for your great work,

Daniel

Adding"µ" char in SpiceEditor regexes

Hello Nuno,

The LTSpice schematic editor automatically replaces the "u" suffix (micro) with a "µ" char (ANSI/0xB5) if the Convert "µ" to "u" option in the Control Panel -> Netlist Options is deselected.
The SpiceEditor.py REPLACE_REGXES currently do not scan for the "µ" char. Propose to add "µ" detection => (Meg|[kmuµnp]).

Thanks for sharing PyLTSpice!

Problems parsing parameters of a subckt

When reading a *.net file using SpiceEditor(), parameters given to a subcircuit are not correctly recognized.

The line on a netlist
XX1 0 N001 sub_test params: Res = 1
results into an malformated result of get_component_info('XX1'):
{'designator': 'XX1', 'nodes': ' 0 N001 sub_test params: Res =', 'value': '1', 'line': 1}

An example netlist file is attached to the issue.

test.net.txt

Read Log Files in Operating Point

Not sure if it is possible as I didn't see it in the docs but I would like to see gm value of a mosfet in op simulation. The gm and more parameters are saved in .log file.

Support for Semiconductor Device Operating Points in the log file.
image

Fix wrong call of UnrecognizedSyntaxError in SpiceEditor

UnrecognizedSyntaxError class is sometimes initiated with wrong number of arguments, which raises TypeError.

Source: PyLtSpice/SpiceEditor.py

class UnrecognizedSyntaxError(Exception):
    """Line doesn't match expected Spice syntax"""
    def __init__(self, line, regex):
        super().__init__(f'Line: "{line}" doesn\'t match regular expression "{regex}"')

Wrong call 1 (line 484) and 2 (line 494):

class SpiceCircuit(object):
    ...
    def setname(self, new_name: str):
        ...
        raise UnrecognizedSyntaxError("Unable to find .SUBCKT clause in subcircuit")
        ...
        raise UnrecognizedSyntaxError("Unable to find .SUBCKT clause in subcircuit")

Wrong call 3 (line 526):

class SpiceCircuit(object):
    ...
    def get_component_info(self, component) -> dict:
        ...
        error_msg = 'Unsupported line "{}"\nExpected format is "{}"'.format(line, REPLACE_REGXES[prefix])
        self.logger.error(error_msg)
        raise UnrecognizedSyntaxError(error_msg)

Negative time sporadically from raw file

Hi.

I've run into a problem. Not sure what is the cause of it. I ran LTspice with the test fixture for the LTC3602 to generate a raw file. Ant then plotted the output voltage with the following code:

import matplotlib.pyplot as plt
from PyLTSpice.LTSpice_RawRead import RawRead as RawRead
file = '3602.raw'
ltr = RawRead(file)
t = ltr['time']
u = ltr['V(in)']
y = ltr['V(out)']
print(t[0:100])
plt.figure()
plt.plot(t,y,'k.-',lw=2,label='Vout')
plt.show()

And it looks like the raw file contains negative number, or PyLTSpice interpret some of the time values as negative numbers. Both the print out of the first values in the time array and plotting the data shows the problem.

This was on a computer running Windows 10, and the latest LTspice (17.0.27.0) and PyLTSpice 1.5 installed using pip.

Using RawWrite to create Noise Simulation Raw Files

RawWrite is not supporting correcting the creation of frequency noise simulation raw files.
If the X axis is supplied as frequency, the RawWrite class is assuming a 'complex' axis, and this is not the case for noise simulations.

Currently (in version 3.1) the workaround consists in forcing the numerical type to double, as exemplified here:

lt_data_write = RawWrite()
lt_data_write.flag_forward = True
lt_data_write.flag_log = True
lt_data_write.flag_fastaccess = False
lt_data_write.plot_name = "Noise Spectral Density - (V/Hz½ or A/Hz½)"
axis_trace = Trace('frequency', lt_data.get_trace('frequency').get_wave(0), numerical_type='double' )  # Fix needs to be introduced here
lt_data_write.add_trace(axis_trace)
lt_data_write.flag_numtype = 'real'  # Adding this in order to correct the wrong assumption made by RawWrite

If creating from another raw file, using the add_traces_from_raw() method works well.

Running under Linux

I have problems running this library under Linux.

I have installed LTspice using wine. And pulled the sources for PyLTspice from github. I changed the definition of LTspice_exe to 'wine ~/.wine/Program Files/LTC... but I can't find a way to get this to run due to the space in the path to LTspice. I could move the location of LTspice, but I don't consider that a good solution.

I think the core issue is the hard coded paths to the location of the LTspice executable. Making this configurable in a way that works on Windows, mac and linux would be awesome.

be able to simulate different '.asc' files in parallel

Discussed in #79

Originally posted by mvanriet January 9, 2023
Now, SimCommander() reads 1 schematic.
You can manipulate this schematic and make a lot of simulations of these variations in parallel.
However, you cannot (it seems), simulate different files in parallel.

I think it can be made possible by not making SimCommander() a derivative of SpiceEditor, and make it a Singleton class (  Singleton Pattern )

Code would look something like this :

sim = SimRunner(output_dir='./sim')

circuit1 = SpiceEditor('test1.asc')
sim.run(circuit1, 'test1A.net')

ciruit1.set_component_value('R1', '1K')
sim.run(circuit1, 'test1B.net')

circuit2 = SpiceEditor('test2.asc')
circuit2.run('test2.net')         # equivalent to sim.run(circuit2,'test2.net')

sim.wait_completion()

The SpiceEditor run function would do this :

  def run( ..................... ):
       runner = SimRunner()
       runner.run( ...................)

So there will always be only 1 simcommander object, and you can simulate any number of circuits, derived from the same .asc file or not. To keep things backwards compatible, the SimCommander() class would still exist as a derivative of SpiceEditor and SimRunner.

Specify work directory to run simulations in

Discussed in #70

Originally posted by mvanriet January 6, 2023
When running a lot of different simulations, you get a lot of output files.
It may be convenient to have these output files in a separate folder.

Workaround on STEP data

LTSpice sometimes repeats the first element of a simulation when operating with .STEP data.
I have placed a workaround to fix this, but, since LTSpice is not always consistent, this sometimes generates erroneous data or causes the script to fail.
Need to find another way of workaround this.

Using set_parameter and PARAM_REGX

Hello,

I'm using PyLTSpice to run kind of automatic simulations of a circuit for different sets of parameters.
When I'm using

LTC = SimCommander("myfile.asc")
LTC.set_parameter('fhp',0.01) 

it doesn't find a good place in the netlist to change the value
An extract of my netlist is :

.param fdsl=fhp/ratio
.param fhp=0.01 ratio=20
.param Cint=940p Rint=1/(2*3.14*fdsl*Cint)" 

Instead of changing the second line, it changes the first one to .param fdsl=fhp=newvalue.
I had a look at the definition of the regex used : PARAM_REGX = r"%s\s*(=\s*)?(?P<value>[\w*/\.+-/{}()]*)"
I fixed my problem by putting the equal sign outside the ()? to impose one '=' character after the parameter name. (PARAM_REGX_NEW = r"%s\s* = (\s*)?(?P<value>[\w*/\.+-/{}()]*)" )
I don't know if this is a good solution to my problem or if I just found some dirty specific fix so I wanted to know your opinion on this matter.

(multiple edits to fix styles problems)

Introduce Github Workflow for Unittest

Hello Nuno,

a next interesting point for the project would be a unit test, where functionality for the python packages are automatically with some test cases are checked. For the sweep_iterators.py I like to setup the github workflow. What do you think?

Best Regards,
Andreas

LTSpice_RawWrite writing bad data for traces.

After downloading this package I tried testing it using the included test script here: PyPi .
The script appears to error out with the following:
Found Numpy. Will be used for storing data Traceback (most recent call last): File "./ltspice_RawWrite_test.py", line 5, in <module> tx = Trace('time', np.arange(0.0, 3e-3, 997E-11)) TypeError: __init__() missing 1 required positional argument: 'data'

When adding the whattype arg to this (Test "real" and "voltage") the script runs and generates a .raw file which once openned up is just very large random pulses:

LTSpice_Converter

raw file name not indexed properly

I am using PyLTSpice to do some automated circuit optimization and ran into an issue with pulling the raw data from batch simulations.

See the output below:

$ pipenv run test.py
Found Numpy. WIll be used for storing data
Creating Netlist
The Netlist was successfully created
Mon Apr  5 12:51:54 2021 : Starting simulation 1
Mon Apr  5 12:51:56 2021: Simulation Successful. Time elapsed 00:00:00:

D:\Documents\test-dir\test-ckt-optimization\circuits\test-ckt-rev-A1_run.raw
D:\Documents\test-dir\test-ckt-optimization\circuits\test-ckt-rev-A1_1.log
Traceback (most recent call last):
  File "D:\Documents\test-dir\test-ckt-optimization\test.py", line 46, in <module>
    result = LTSpiceRawRead(raw)
  File "C:\Users\luke\.virtualenvs\test-ckt-optimization-vibq49oT\lib\site-packages\PyLTSpice\LTSpice_RawRead.py", line 264, in __init__
    raw_file_size = os.stat(raw_filename).st_size  # Get the file size in order to know the data size
FileNotFoundError: [WinError 2] The system cannot find the file specified: 'D:\\Documents\\test-dir\\test-ckt-optimization\\circuits\\test-ckt-rev-A1_run.raw'

I can remedy the issue by doing a string replace of the raw file to replace _run.raw with _{index}.raw and get the behavior I want. Looking into the source, its seems that the raw file output is not updated like the log file output name is in LTCommander::run

    def run(self, run_id=None):
        """
        Executes a simulation run with the conditions set by the user. (See also set_parameter, set_component_value,
        add_instruction)
        The run_id parameter can be used to override the naming protocol of the log files.
        :return (raw filename, log filename) if simulation is successful else (None, log file name)
        """
        # update number of simulation
        self.runno += 1  # Using internal simulation number in case a run_id is not supplied

        # decide sim required
        if self.netlist is not None:
            # Write the new settings
            run_netlist_file = "%s_%i.net" % (self.circuit_radic, self.runno)
            self.write_netlist(run_netlist_file)
            cmd_run = LTspice_exe + LTspice_arg.get('run') + [run_netlist_file]

            # run the simulation
            start_time = clock_function()
            print(time.asctime(), ": Starting simulation %d" % self.runno)

            # start execution
            retcode = run_function(cmd_run)

            # process the logfile, user can rename it
            dest_log = "%s_%i.log" % (self.circuit_radic, self.runno)
            # print simulation time
            sim_time = time.strftime("%H:%M:%S", time.gmtime(clock_function() - start_time))
            # handle simstate
            if retcode == 0:
                # simulation succesfull
                print(time.asctime() + ": Simulation Successful. Time elapsed %s:%s" % (sim_time, END_LINE_TERM))
                self.write_log("%d%s" % (self.runno, END_LINE_TERM))
                self.okSim += 1
            else:
                # simulation failed
                self.failSim += 1
                # raise exception for try/except construct
                # SRC: https://stackoverflow.com/questions/2052390/manually-raising-throwing-an-exception-in-python
                # raise ValueError(time.asctime() + ': Simulation number ' + str(self.runno) + ' Failed !')
                print(time.asctime() + ": Simulation Failed. Time elapsed %s:%s" % (sim_time, END_LINE_TERM))
                # update failed parameters and counter
                dest_log += 'fail'

            try:
                os.replace(self.circuit_radic + '_run.log', dest_log)
            except FileNotFoundError:
                pass

            if retcode == 0:  # If simulation is successful
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                return self.circuit_radic + '_run.raw', dest_log  # Return rawfile and logfile if simulation was OK
+++++++++++++++++++++++ CHANGE TO ++++++++++++++++++++++++++++++++++++++++++
                raw_name = self.circuit_radic + '_' + str(self.runno) + '.raw', dest_log
                return raw_name, dest_log
            else:
                return None, dest_log
        else:
            # no simulation required
            raise UserWarning('skipping simulation ' + str(self.runno))

after doing the file name string replacement I can get clean output

$ pipenv run test.py
Found Numpy. WIll be used for storing data
Creating Netlist
The Netlist was successfully created
Mon Apr  5 12:55:24 2021 : Starting simulation 1
Mon Apr  5 12:55:26 2021: Simulation Successful. Time elapsed 00:00:00:

D:\Documents\test-dir\test-ckt-optimization\circuits\test-ckt-rev-A1_run.raw
D:\Documents\test-dir\test-ckt-optimization\circuits\test-ckt-rev-A1_1.log
Normal access
['frequency', 'gain', 'V(d1.rs)', 'V(d1.sid)', 'V(d1.fid)', 'V(d1)', 'V(r9)', 'V(r3)', 'V(r12)', 'V(r11)', 'V(r8)', 'V(r7)', 'V(r6)', 'V(r5)', 'V(r4)', 'V(r2)', 'V(r1)', 'V(onoise)', 'inoise']

Just thought I would notify of the bug here, or perhaps I am misunderstanding the module a tad.

Application Example in LTSpiceBatch.py fails

Hi again, Nuno!

I am wondering about the following: When using the given example at the top of the LTSpiceBatch.py, the generation of self.circuit_radic will point to what would be the root directory in Linux - actually, I am not sure what this is interpreted as on Windows. Anyway - it is not pointing to the same directory as the python script and the LTSpice schematic file in this case and throws a "no permission" error.
A test showed that the following if-condition helps:
'''
if circuit_path == '':
circuit_path = '.'
'''

Best regards,

Daniel

LTspice executable not settable

The LTspice path inside the PyLTSpice.sim.ltspice_simulator module doesn't allow the user to set the name of the executable only the folder. The executable file name has changed in the newer version of LTspice and thus breaks:

spice_exe = ["wine", os.environ['LTSPICEFOLDER'] + "/XVIIx64.exe"]

I tried doing a work around by creating a custom LTspice class, but this doesn't work for some reason. Apologies if I have made an oversight.

from PyLTSpice.sim.ltspice_simulator import LTspice
class CustomLTspice(LTspice):
    spice_exe = [
        "wine",
        "/home/aanas/.wine/drive_c/Program Files/ADI/LTspice/Ltspice.exe",
        "-b",
    ]
    process_name = "Ltspice.exe"

Only way I got it running was by reloading the module with the modified class:

ltspice_module = sys.modules["PyLTSpice.sim.ltspice_simulator"]
ltspice_module.LTspice = CustomLTspice

I believe the LTSPICECOMMAND for "/XVIIx64.exe" should be an environment variable

Read measurement results from log file after simulating with simrunner

I would like to start a Simulation via SimRunner and get a signal via the .MEAS-Function in my .LOG -file
When simulating the LT-Spice files via the "SimRunner", the "Meas-Instructions" are taken over into the newly created netlist, but the result is not displayed in the log file. Instead a "Measurement failed" message is displayed.
Also via the "add_instructions" function no result of the "Meas" function can be displayed in the log file.

from PyLTSpice.LTSteps import LTSpiceLogReader not working

When trying to import "from PyLTSpice.LTSteps import LTSpiceLogReader" I get the following error:

Exception has occurred: EncodingDetectError
Expected string "Circuit:" not found in file:SpiceBatch.log
File "---my path---", line 4, in
from PyLTSpice.LTSteps import LTSpiceLogReader
PyLTSpice.utils.detect_encoding.EncodingDetectError: Expected string "Circuit:" not found in file:SpiceBatch.log

I am working with the newest PyLTSpice 4.0.2. When I downgrade to Version 3.1 it works perfectly.

_getline_startingwith in SpiceEditor.py returns wrong reference designators

Use case: Search for R11 in a netlist that includes both R112 and R11. I find that this function returns R112 instead of R11. I changed the following line and the issue was resolved. I encountered this issue while I was trying to change the value of R11 and instead it changed the value of R112.

if line_upcase.startswith(substr_upper):

becomes

if line_upcase.startswith(substr_upper) and "%s "%substr_upper in line.upper():

Can't load .raw binary file

Hi @nunobrum,

First of all, I want to thank you for this awesome piece of code! It was exactly what I was looking for and hopefully lots of other people will be helped too with your code.

I tested your approach with a very simple example (almost the same in your blog) just to test the functionality of loading a .raw file. Everything works just fine when I load a .raw ASCII file, but when trying to load a .raw binary file, things got weird and I really don't know what to do, probably my fault.

Here is the code snippet and the following error:

import LTSpice_RawRead
import matplotlib.pyplot as plt

LT = LTSpice_RawRead.LTSpiceRawRead('D:/Gustavo/MEGA/PyLTSpice-master/teste.raw')
IR1 = LT.get_trace("V(vsw)")
x = LT.get_trace(0)  # Zero is always the X axis
steps = LT.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()

And that's the output error message:

Found Numpy. WIll be used for storing data
Binary file
Normal access
Traceback (most recent call last):
  File "D:\Gustavo\MEGA\PyLTSpice-master\test_script.py", line 4, in <module>
    LT = LTSpice_RawRead.LTSpiceRawRead('D:/Gustavo/MEGA/PyLTSpice-master/teste.raw')
  File "D:\Gustavo\MEGA\PyLTSpice-master\LTSpice_RawRead.py", line 331, in __init__
    var.set_pointB(point, value)
  File "D:\Gustavo\MEGA\PyLTSpice-master\LTSpice_RawRead.py", line 75, in set_pointB
    self.data[n] = unpack("d", value)[0]
struct.error: unpack requires a buffer of 8 bytes

If necessary, I can also upload my .raw file. Best regards and Thank you very much.

Issue using Single point AC simulation

I am working on an AC circuit simulation with a single frequency point at the time and I have encontered a bug in the LTSpiceRawRead function. Here is a small python file and netlist file to replicate the issue

netlist file: simple_net.net

Title_file
c1 1 0 1p
c2 1 0 1p
vin 1 0 ac 1 dc 0
.ac dec 1 10 10
.probe
.end

python file: lts_bug_exemple.py

from PyLTSpice.LTSpiceBatch import SimCommander
from PyLTSpice.LTSpice_RawRead import LTSpiceRawRead
from matplotlib import pyplot as plt
from numpy import abs, pi

file_net = "simple_net.net"
LTC = SimCommander(file_net)
LTC.run()
LTC.wait_completion()

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

raw_out_file = file_net[:-4] + "_1.raw"
LTR = LTSpiceRawRead(raw_out_file)

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

list_data = ["I(Vin)", "V(1)", "frequency"]
dict_out = {}
for key in list_data:
    dict_out[key] = LTR.get_trace(key)
    
steps = LTR.get_steps()
current_freq = dict_out["frequency"].get_wave()
current_z = dict_out["V(1)"].get_wave() / abs(dict_out["I(Vin)"].get_wave())
plt.plot(current_freq, current_z, label="Impedance 1-0")
plt.xscale('log')
plt.yscale('log')
plt.legend()
plt.grid(True)
plt.show()

These files give me the following error:

Found Numpy. WIll be used for storing data
Wed Jun 15 17:21:33 2022 : Starting simulation 1
Wed Jun 15 17:21:33 2022: Simulation Successful. Time elapsed 00:00:00:

No Callback
Successful/Total Simulations: 1/1
Normal access
Traceback (most recent call last):
  File "ltspice_exemple.py", line 56, in <module>
    LTR = LTSpiceRawRead(raw_out_file)
  File "C:\Tools\Dev\Anaconda2\envs\myenv\lib\site-packages\PyLTSpice\LTSpice_RawRead.py", line 672, in __init__
    var.set_pointB16(point, value)
  File "C:\Tools\Dev\Anaconda2\envs\myenv\lib\site-packages\PyLTSpice\LTSpice_RawRead.py", line 279, in set_pointB16
    self.data[n] = complex(re, im)
TypeError: can't convert complex to float

Note that if the line .ac dec 1 10 10 in file simple_net.net is changed to .ac dec 10 10 100k, no bug is raised and the program is working.

If you have any element that may help in solving this issue, please let me know.

LTSpice_Batch under macOS

Hi Nuno,
I am trying to get to work your beautiful toolchain under macOS Catalina (10.15.7) in Jupyter notebook (Python version: 3.8.5) with LTspice XVII for OS X, Build Mar 3 2021 Version:17.0.23.0
I replaced line 37 in LTSpiceBatch.py with the macOS equivalent which gets rid of the FileNotFoundError message:

# Windows based original
#LTspiceIV_exe = [r"C:\Program Files (x86)\LTC\LTspiceIV\scad3.exe"]
LTspiceIV_exe = [r"/Applications/LTspice.app/Contents/MacOS/LTspice"]

I then tried to run the LTSpice_Batch example with my file Draft1.asc, which contains a simple voltage divider for testing purposes:

from PyLTSpice.LTSpiceBatch import LTCommander
from shutil import copyfile

# get script absolute path
meAbsPath = os.path.dirname(os.path.realpath("Draft1.asc"))
# select spice model
LTC = LTCommander(meAbsPath + "/Draft1.asc")

which opens a new instance of LTSpice but does not open the file. It only displays the start screen where you can either choose to open a new file or a recent file etc.
The output is:

Creating Netlist

After manually exiting LTSpice, the output generates

The Netlist was successfully created

The LTC object contains the correct path to the .net file (from LTC.netlist_file) but the netlist ist empty (from LTC.netlist)

Aborting the process results in the following output:

---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-67-1caf2e289a03> in <module>
      5 meAbsPath = os.path.dirname(os.path.realpath("Draft1.asc"))
      6 # select spice model
----> 7 LTC = LTCommander(meAbsPath + "/Draft1.asc")

~/opt/anaconda3/lib/python3.8/site-packages/PyLTSpice/LTSpiceBatch.py in __init__(self, circuit_file)
    719         warn("Deprecated Class. Please use the new SimCommander class instead of LTCommander\n"
    720              "For more information consult. https://www.nunobrum.com/pyspicer.html", DeprecationWarning)
--> 721         SimCommander.__init__(self, circuit_file, 1)
    722 
    723 

~/opt/anaconda3/lib/python3.8/site-packages/PyLTSpice/LTSpiceBatch.py in __init__(self, circuit_file, parallel_sims)
    270 
    271             print("Creating Netlist")
--> 272             retcode = run_function(cmd_netlist)
    273             if retcode == 0:
    274                 print("The Netlist was successfully created")

~/opt/anaconda3/lib/python3.8/site-packages/PyLTSpice/LTSpiceBatch.py in run_function(command)
    107 
    108     def run_function(command):
--> 109         result = subprocess.run(command)
    110         return result.returncode
    111 else:

~/opt/anaconda3/lib/python3.8/subprocess.py in run(input, capture_output, timeout, check, *popenargs, **kwargs)
    489     with Popen(*popenargs, **kwargs) as process:
    490         try:
--> 491             stdout, stderr = process.communicate(input, timeout=timeout)
    492         except TimeoutExpired as exc:
    493             process.kill()

~/opt/anaconda3/lib/python3.8/subprocess.py in communicate(self, input, timeout)
   1014                 stderr = self.stderr.read()
   1015                 self.stderr.close()
-> 1016             self.wait()
   1017         else:
   1018             if timeout is not None:

~/opt/anaconda3/lib/python3.8/subprocess.py in wait(self, timeout)
   1077             endtime = _time() + timeout
   1078         try:
-> 1079             return self._wait(timeout=timeout)
   1080         except KeyboardInterrupt:
   1081             # https://bugs.python.org/issue25942

~/opt/anaconda3/lib/python3.8/subprocess.py in _wait(self, timeout)
   1802                         if self.returncode is not None:
   1803                             break  # Another thread waited.
-> 1804                         (pid, sts) = self._try_wait(0)
   1805                         # Check the pid and loop as waitpid has been known to
   1806                         # return 0 even without WNOHANG in odd situations.

~/opt/anaconda3/lib/python3.8/subprocess.py in _try_wait(self, wait_flags)
   1760             """All callers to this function MUST hold self._waitpid_lock."""
   1761             try:
-> 1762                 (pid, sts) = os.waitpid(self.pid, wait_flags)
   1763             except ChildProcessError:
   1764                 # This happens if SIGCLD is set to be ignored or waiting

KeyboardInterrupt: 

I tried to manually add the .net file to LTC.netlist and change the value of resistor R2 with LTC.set_component_value('R2', '10k'). The changed value is visible in LTC.netlist. Running LTC.reset_netlist() does not produce any errors but raw, log = LTC.run()does. There seems to be an error where the path to the file occurs twice and the file itself is addressed as Draft1_1.net.

---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-10-c18aa03a086d> in <module>
----> 1 raw, log = LTC.run()
      2 LTC.wait_completion()

~/opt/anaconda3/lib/python3.8/site-packages/PyLTSpice/LTSpiceBatch.py in run(self, run_id)
    747             # Write the new settings
    748             run_netlist_file = "%s_%i.net" % (self.circuit_radic, self.runno)
--> 749             self.write_netlist(run_netlist_file)
    750             cmd_run = LTspice_exe + LTspice_arg.get('run') + [run_netlist_file]
    751             print("cmd_run: " +str(cmd_run))

~/opt/anaconda3/lib/python3.8/site-packages/PyLTSpice/LTSpiceBatch.py in write_netlist(self, run_netlist_file)
    601             if not line.endswith(END_LINE_TERM):
    602                 self.netlist[i] = line + END_LINE_TERM
--> 603         f = open(run_netlist_file, 'w')
    604         print("Self netlist: " +str(self.netlist))
    605         f.writelines(self.netlist)

FileNotFoundError: [Errno 2] No such file or directory: 'path_to_file/path_to_file_again/Draft1_1.net'

At this point I am out of ideas how to solve this issue and would appreciate help.

Cannot parse resistors with voltage-based value "R={V(vload)}"

To create things like dynamic loads, one approach is to set a resistor's value equal a voltage, which LTSpice effectively casts volts --> ohms. To achieve this, the resistor's value must be set as R={V(vload)}, assuming vload was the name of the net with the associated voltage (e.g. 1V becomes 1 ohm).

Presently, this throws an error. I believe line 90 in /sim/spice_editor.py should be changed from:
'R': r"^(?P<designator>R§?\w+)(?P<nodes>(\s+\S+){2})(?P<model>\s+\w+)?\s+(?P<value>({)?(?(6).*}|([0-9\.E+-]+(Meg|[kmuµnpf])?R?)\d*)).*$", # Resistors

to

'R': r"^(?P<designator>R§?\w+)(?P<nodes>(\s+\S+){2})(?P<model>\s+\w+)?\s+(?P<value>(R=)?({)?(?(7).*}|([0-9\.E+-]+(Meg|[kmuµnpf])?R?))).*$", # Resistors

to correct this. Specifically, adding (R=)? then changing the check for group 6 to group 7.

I'm running version 2.1, however the issue seems to still exist in latest version.

LookupError: unknown encoding: ansi in detect_encoding.py

Hello, thank you for this great piece of software.

While trying to load a manually modified SPICE netlist into SimCommander, I encountered the following error:
File "/opt/homebrew/lib/python3.10/site-packages/PyLTSpice/detect_encoding.py", line 37, in detect_encoding with open(file_path, 'r', encoding=encoding) as f: LookupError: unknown encoding: ansi
The error being that there is no encoding named 'ansi' in Python 3 (there may have been in Python 2). I suggest simply removing 'ansi' from the list of encodings that are iterated over in detect_encoding.py. Also, shift-jis is misspelled (should be shift_jis). List of available encodings can be found here: https://docs.python.org/2/library/codecs.html#standard-encodings

(the reason I ran into this error, despite my netlist being utf-8 encoded, was that my file did not have a comment at the beginning of the file, which broke detect_encoding since it relies on finding a * at the beginning of the file to determine file encoding. I do not know if it is required to have a comment at the beginning of any SPICE netlist, however it is indeed required for SimCommander to be able to read it).

thanks,

Deprecated examples in readme.md

Hi,

I'm not used to the github/pullrequest/... workflow, but I'd like to contribute my stuff to this awesome package.
I couldn't run the examples without change. I'm using Python 3.9.

Example1

In https://github.com/nunobrum/PyLTSpice#ltspice_rawreadpy

from matplotlib import plot
#change to
from matplotlib import pyplot as plt

Example 2

Next example does not work at all anymore. I guess code changed a lot

raw, log = LTC.run()

I'd like to request this as a feature. It makes so much more easy for people who don't want to deal with events and can spare the time. Either raw/log file or any other id

run1 = LTC.run()
# ...
run2 = LTC.run()
LTC.wait_completion()

ltr1 = LTSpiceRawRead(run1)
# ...
ltr1 = LTSpiceRawRead(run2)

Regression in set_component_value function when line contains a parameter substitution

Steps to reproduce:

(venv) C:\work>pip install git+https://github.com/nunobrum/PyLTSpice.git#egg=PyLTSpice
...
(venv) C:\work>python
Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from PyLTSpice.LTSpiceBatch import SimCommander
>>> LTC = SimCommander("my_circuit.asc")
Creating Netlist
The Netlist was successfully created
>>> LTC.add_instruction("R1 A B {Rparam}")
>>> LTC.set_component_value("R1", 1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\work\venv\lib\site-packages\PyLTSpice\SpiceEditor.py", line 383, in set_component_value
    self._set_model_and_value(device, value)
  File "C:\work\venv\lib\site-packages\PyLTSpice\SpiceEditor.py", line 262, in _set_model_and_value
    raise NotImplementedError('Unsupported line "{}"\nExpected format is "{}"'.format(line, regxstr))
NotImplementedError: Unsupported line "R1 A B {Rparam}
"
Expected format is "^(R\w+)(\s+[\w\+\-]+){2}\s+(?P<value>({)?(?(3).*}|([0-9\.E+-]+(Meg|[kmunp])?R?))).*$"
>>>

Expected behavior:

(venv) C:\work>pip install PyLTSpice==1.1
...
(venv) C:\work>python
Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from PyLTSpice.LTSpiceBatch import SimCommander
>>> LTC = SimCommander("my_circuit.asc")
Creating Netlist
The Netlist was successfully created
>>> LTC.add_instruction("R1 A B {Rparam}")
>>> LTC.set_component_value("R1", 1)
>>>

It appears that in 56c9f65, the group number wasn't updated in the conditional for the 'R','L','C' in REPLACE_REGXES in PyLTSpice/SpiceEditor.py.

What to do if SPICE Circuit does not behave well ito convergence?

Dear nunobrum,

This is more a question than an issue.
I have built a fairly complex SPICE circuit. With some certain parameter configurations, it will take ages to converge or won't converge at all. Is there a simple way to make PyLTSpice identify such problems (fixed max simulation duration) and abort the one single problematic simulation and carry on with the next, ideally saving a raw file containing the data that has been recorded up to the point of abortion?
Best regards,

danielphili

set_parameter does not handle floating numbers

Hello Nuno,

When I use floating numbers with set_parameter, the netlist ends up with extra decimal separtors (e.g. : ".param I1=0.5.1").
If I'm not mistaken, the regexp is looking for alphanumeric characters (\w) but does not include the point, which makes the construction of the new line in the netlist a bit awkward.

 import re

# subsitute for set_parameters inputs
param = "I1"
value = "0.5"
line = ".PARAM I1=1.0"

regx = re.compile(r"%s\s*=\s*(\w*)" % param, re.IGNORECASE)
#line = self.netlist[param_line]
m = regx.search(line)
start, stop = m.span()
#self.netlist[param_line] = line[:start] + "{}={}".format(param, value) + line[stop:]
print(line[:start] + "{}={}".format(param, value) + line[stop:])

=> .PARAM I1=0.5.0

At first sight, the easiest way to fix it would be to simply build a new param line from the inputs, e.g. :
self.netlist[param_line] = ".param " + "{} = {}".format(param, value)

But maybe, there is a case that I don't know and which would benefit from an enhanced regexp...

Best Regards,
Jak

Licensing inconsistent across files

A variety of license terms are used across project files, including:

  • Free
  • lGPL v3
  • General Public GNU License
  • GPL v3
  • blank

The intent appears to be a uniform GPL v3. Suggest all code file headers either:

  1. state Licence: GPL v3 or similar according to existing convention
  2. refer to the LICENSE file

cleaning up files after simulation

Discussed in #71

Originally posted by mvanriet January 6, 2023
If the results of a simulation are processed automatically, the raw-files are often not needed any more.
So a function like delete_simulation_files() or so could be handy.

Changing Component Value to 0

I discovered an issue when setting a component's value via SimCommander.set_component_value() to 0. Calculating log(0) is a bad idea...
It's a quick fix though and I've already done it in my dev environment. It's best to incorporate it into the main release though.

def format_eng(value):
    if value == 0.0:
        e = 0
        suffix = ''
    else:
        e = floor(log(abs(value), 1000))
        if -4 <= e < 0:
            suffix = "pnum"[e]
        elif e == 0:
            suffix = ''
        elif e == 1:
            suffix = "k"
        elif e == 2:
            suffix = 'Meg'
        else:
            return '{:E}'.format(value)

    return '{:g}{:}'.format(value* 1000**-e, suffix)

Interpretation / precision of time instances

Not sure if this is a precision issue but did a transient simulation with a duration of just a few microseconds. Extracted the result with

LTR = RawRead(filename_raw)
tTrc = LTR.get_trace('time')
t = 1e6*tTrc.get_wave() #multiply by 1e6 to get time in us
UcTrc = LTR.get_trace('V(c)')
Uc = UcTrc.get_wave() # in V
print(t[0:50]) #print the values

When I look at the printed values of t, I see that there are wrong values (with negative sign) in there which causes the plotting going crazy =)
The print command above gives me e.g.

[ 0. 1. 1.0000022 1.0000066 1.0000154 1.00002634 1.00004821 1.00009197 1.00017773 1.00031161 1.00053561 1.00059758 1.00072151 1.00075214 1.00081341 1.00087747 1.001 1.00103676 1.00108577 1.00118379 1.00137984 1.00173638 1.00220805 1.00267068 1.00359595 1.00444282 1.00588001 1.00745261 1.00830441 1.00916114 1.01001728 1.01094935 1.01233256 1.015099 1.02063186 1.03039749 1.04016311 1.04992874 1.05969436 1.06945999 1.08899124 1.11828811 1.14758499 -1.24035843 1.33313186 -1.53332718 1.73352249 -2.15832718 2.58313186 3.001 ]

Maybe this is not even PyLTSpice related, but any idea what it going on here with the interpretation of the time values?

Component Names not Starting with Correct Letter

I am using your library to simulate a given schematic through Python. Some components in there do not follow naming conventions. E.g. one voltage source is called Input_V_Bat. In the netlist this results in a line starting with V§Input_V_Bat. Applying your regular expressions won't work in that case.

While users should probably follow naming conventions, I think it's best to incorporate also cases in which they don't like mine.

Changing

REPLACE_REGEXES = {
    'V': r"^(V\w+)(\s+\S+){2}\s+(?P<value>.*)$",
}

in SpiceEditor.py to 'V': r"^(V\S+)(\s+\S+){2}\s+(?P<value>.*)$", did the trick. Not sure though if this is the best solution.

Thanks for your great work and cheers!

Feature Proposal: multithreaded batched SPICE simulation

Dear Nuno,

Now that you moved to creating one netlist file per parameterized simulation, maybe there is a way to launch multiple instances of LTSpice working off the list of batched simulations.
However, I can well understand that introducing multithreaded simulation calls might mean a lot of work and also a source of instability - but might be interesting to think about since especially server architectures that are often used for simulations will offer many cores of which LTSpice always only uses one.
Best regards,

Daniel

Spicelines misinterpreted in get_component_info

Spicelines that are added to a part are not correctly interpreted by the get_component_info method. On some items such as voltage sources, the spice lines are added to the value (such as a voltage source, see example), on other parts they are ignored. Ideally, there should be a way to get these extra spice lines.

Use case: pulling a bunch of tolerances for a worst case analysis

example code:

# select spice model
LTC = SimCommander("voltage_divider.asc")
# set default arguments
LTC.set_parameters(res=0, cap=1e-15)
#get the parts
part_list = LTC.get_components()
for part in part_list:
    #get the part info
    part_info = LTC.get_component_info(part)
    print(f"{part} value is {part_info}")

Output:

R1 value is {'designator': 'R1', 'nodes': ' N001 out', 'model': None, 'value': '1k', 'line': 1}
R2 value is {'designator': 'R2', 'nodes': ' out 0', 'model': None, 'value': '1k', 'line': 2}
C1 value is {'designator': 'C1', 'nodes': ' out 0', 'model': None, 'value': '1µ', 'line': 3}
V1 value is {'designator': 'V1', 'nodes': ' N001 0', 'value': '5 tol=1', 'line': 4}

Sample circuit:
voltage_divider.asc

Version 4
SHEET 1 880 680
WIRE 160 32 -16 32
WIRE 160 64 160 32
WIRE -16 96 -16 32
WIRE 160 160 160 144
WIRE 192 160 160 160
WIRE 240 160 192 160
WIRE -16 192 -16 176
WIRE 240 240 240 224
WIRE 240 240 160 240
WIRE 160 272 160 240
FLAG 160 272 0
FLAG -16 192 0
FLAG 192 160 out
SYMBOL res 144 48 R0
WINDOW 0 -42 46 Left 2
WINDOW 3 -41 76 Left 2
WINDOW 39 -57 99 Left 2
SYMATTR InstName R1
SYMATTR Value 1k
SYMATTR SpiceLine tol=1
SYMBOL res 144 144 R0
WINDOW 0 -44 51 Left 2
WINDOW 3 -42 74 Left 2
WINDOW 39 -62 95 Left 2
SYMATTR InstName R2
SYMATTR Value 1k
SYMATTR SpiceLine tol=1
SYMBOL cap 224 160 R0
WINDOW 39 24 84 Left 2
SYMATTR InstName C1
SYMATTR Value 1u
SYMATTR SpiceLine tol=10
SYMBOL voltage -16 80 R0
WINDOW 39 24 124 Left 2
SYMATTR InstName V1
SYMATTR Value 5
SYMATTR SpiceLine tol=1

run() does not return correct filename

I use the examples form the README file. The extract below shows the critical code:
raw, log = LTC.run()
LTC.wait_completion()
LTR = LTSpiceRawRead(raw)

The filename reported by run() in raw is not what the simulation generates on the disk. If the simulation is named "Test.asc", the rawfile on disk is named "Test.raw", but run() returns "Test_run.raw".

This error is present both running under windows and linux. On windows I run the latest version from pypi (PyLTSpice-1.4-py3-none-any.whl), on linux I run a slightly modified 1.4 version (commit 265dfa3).

Call to add_LTspiceRunCmdLineSwitches result in error when subsequently calling run()

I'm trying to add LTSpice command line switches using the add_LTspiceRunCmdLineSwitches(self, *args) function of the PyLTSpice.LTSpiceBatch.SimCommander class.

When using add_LTspiceRunCmdLineSwitches subsequent calls to the run() function fail with the following error:
TypeError: can only concatenate list (not "tuple") to list.

I'm using PyLTSpice in a python3 environment. The problem seems to be related to the fact that the function argument *arg used in add_LTspiceRunCmdLineSwitches is (always) being transferred as being of type tuple (not as list)

As temporary workaround I'm now using add_LTspiceRunCmdLineSwitches(self, args), hence the asterix before args removed.
This works in case of using a single command line argument (might fails in case of more than one command line arguments are used).

Make SimCommander work on systems with non-standard installation of LTSpice

In order to use SimCommander on systems with non-standard installation of LTSpice there's already method Method setLTspiceRunCommand.

But unfortunately this cannot be used. It's kind of hidden by the constructor __init__. (Currently class SimCommander defines only that one constructor.) It is because the constructor, so during object creation, already uses the internal variable LTspice_exe and tries to create a process from it. That is BEFORE the user can change the executable's name via a call of the object's function setLTspiceRunCommand.

There are different ways how to solve it. I'd like not to suggest any.

Here I'd like to publish my WORKAROUND how to create a code which runs on different machines with different kinds of installations. As soon there's any PyLTSpice version available at https://pypi.org/project/PyLTSpice/ I'll remove my workaround.

# WORKAROUND because of not-working function setLTspiceRunCommand() of PyLTSpice 2,3, 3.0

from typing import Union
from shutil import which



def getLTspiceRunCommand():
    """Find LTSpice installation on system (in PATH variable)"""
    run_commands_to_check = [
        'XVIIx64',                 # 64-bit installer for windows from https://www.analog.com/en/design-center/design-tools-and-calculators/ltspice-simulator.html
        'ADI LTspice XVII',        # Scoop package manager via bucket https://github.com/SayCV/scoop-cvp
    ]
    return next(c for c in run_commands_to_check if which(c))

run_command = getLTspiceRunCommand()

if not run_command:
    print("ERROR: No LTSpice found on system. Please install it.") 


# WORKAROUND for the problem that setLTspiceRunCommand cannot be used as long as default installation path is not present on the system. (in parallel to possible others). Reason: The Constructor of SimCommander executes the LTSpice executable. This is BEFORE the LTSpice executable name can be set from user via call of method setLTspiceRunCommand:

class SimCommander_setLTspiceRunCommand(SimCommander):
    
    def __init__(self, run_command: Union[str, list]):
            super().setLTspiceRunCommand(run_command)

SimCommander_setLTspiceRunCommand(run_command)

# END OF WORKAROUND


# Now constructor uses variable LTspice_exe pointing to existing executable.
LTC = SimCommander(schematic.asc')

Workaround on terminating simulation

I found terminating simulation comes in handy when operating with a large batch of simulations and it seems to me that this library doesn't provide a way for me to do this.

Here is the modified example code provided by PyLTSpice which will terminate the simulation when it take more then 10 seconds to run.


import os
from PyLTSpice.LTSpiceBatch import SimCommander

#library needed to termaiate LTspice in windows
from time import sleep
import psutil

#Function to termaiate LTspice in windows
def KillLTspice():
    PROCNAME = "XVIIx64.exe"

    for proc in psutil.process_iter():
        # check whether the process name matches
        
        if proc.name() == PROCNAME:
            print("killing ltspice")
            proc.kill()
#end function

def processing_data(raw_file, log_file):
    print("Handling the simulation data of %s, log file %s" % (raw_file, log_file))

# select spice model
LTC = SimCommander("Batch_Test.asc")
# set default arguments
LTC.set_parameters(res=0, cap=100e-6)
LTC.set_component_value('R2', '2k')
LTC.set_component_value('R1', '4k')
LTC.set_element_model('V3', "SINE(0 1 3k 0 0 0)")
# define simulation
LTC.add_instructions(
    "; Simulation settings",
    ".param run = 0"
)

LTC.reset_netlist()
LTC.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"
)

LTC.run()
#LTC.wait_completion() # this line is no longer needed
timeout=10 #10 seconds timeout used to test out this function
TimeoutCount=0
#The code below are mainly copy form wait_completion() function
LTC.updated_stats()
while len(LTC.threads) > 0 and TimeoutCount<timeout: #added stop condition: break the loop after timeout
    TimeoutCount+=1 # line added to achieve this function
    sleep(1)
    LTC.updated_stats()
	
KillLTspice()# call termaiation function, will do nothing if the simulation exit in time.

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

Holp you find this code useful and may become on of the functions of PyLTspice.

random time values interpreted as negative values

Draft4.zip
Dear Nunobrum,

Thanks for taking the effort of having started and maintaining this project.
I have following problem:

Somehow, some of time values in my raw file are being interpreted as below zero (which does not make sense in this case).
The error occurs having simulated a simple circuit without any ".step param" statement or similar using LTSpiceIV.
It would be very nice if you could try to reproduce the error:

  1. simulate the schematic "Draft4.asc" with LTSpiceIV and copy the "Draft4.raw" file to your "tests" folder.
  2. In your "test_rawread.py" file, replace
    files = [ 'tran.raw', 'tran - step.raw', 'ac.raw', 'ac - step.raw', ]
    with files = ['Draft4.raw'] and execute "test_rawread.py".
  3. If you encounter negative values in time, you have reproduced the error.

Why I think this is critical, is that I cannot be sure if the time values are maybe not the only ones interpreted falsely negative. It does not seem like this is the case, however, since the wrong negative interpretation seems to be kind of rare (only few values), they might just be hidden in the simulation data where many values are so small you could not recognize them being falsely negative.

Thanks a lot for considering my issue!
Best regards,

Daniel (danielphili)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.