From bf01f0ceb867f7aa840ca192b4d9f089b864360b Mon Sep 17 00:00:00 2001 From: nunobrum Date: Sun, 14 May 2023 21:09:32 +0200 Subject: [PATCH 1/2] Removing LTspice simulator from __init__.py which was generating unneeded prints --- PyLTSpice/__init__.py | 1 - PyLTSpice/rawplot.py | 84 ++++++++-------- PyLTSpice/simulator.py | 215 ----------------------------------------- tests/batch_test3.py | 4 +- tests/batch_test4.py | 6 +- tests/fra_example.py | 2 +- 6 files changed, 45 insertions(+), 267 deletions(-) delete mode 100644 PyLTSpice/simulator.py diff --git a/PyLTSpice/__init__.py b/PyLTSpice/__init__.py index 40e7ae7..696c63b 100644 --- a/PyLTSpice/__init__.py +++ b/PyLTSpice/__init__.py @@ -6,4 +6,3 @@ from .sim.spice_editor import SpiceEditor, SpiceCircuit from .sim.sim_runner import SimRunner from .sim.sim_batch import SimCommander -from .sim.ltspice_simulator import LTspice diff --git a/PyLTSpice/rawplot.py b/PyLTSpice/rawplot.py index 5be3a7a..76ca7c4 100644 --- a/PyLTSpice/rawplot.py +++ b/PyLTSpice/rawplot.py @@ -26,11 +26,12 @@ def main(): """Uses matplotlib to plot the data in the raw file""" import sys + import matplotlib import matplotlib.pyplot as plt import os from os.path import split as path_split from os.path import join as path_join - from numpy import abs as mag + from numpy import abs as mag, angle as phase_np def what_to_units(whattype): """Determines the unit to display on the plot Y axis""" @@ -40,10 +41,14 @@ def what_to_units(whattype): return 'A' directory = os.getcwd() + matplotlib.use('wxagg') if len(sys.argv) > 2: raw_filename = sys.argv[1] trace_names = sys.argv[2:] + elif len(sys.argv) > 1: + raw_filename = sys.argv[1] + trace_names = '*' # All traces else: print("Usage: rawplot.py RAW_FILE TRACE_NAME") print("TRACE_NAME is the traces to plot") @@ -64,57 +69,46 @@ def what_to_units(whattype): steps_data = [0] print("Steps read are :", list(steps_data)) - if 'complex' in LTR.flags: - n_axis = len(traces) * 2 - else: - n_axis = len(traces) + n_axis = len(traces) fig, axis_set = plt.subplots(nrows=n_axis, ncols=1, sharex='all') - write_labels = True + + if n_axis == 1: + axis_set = [axis_set] # Needs to have a list of axis for i, trace in enumerate(traces): - if 'complex' in LTR.flags: - axis_set = axis_set[2 * i: 2 * i + 2] # Returns two axis - else: - if n_axis == 1: - axis_set = [axis_set] # Needs to return a list + ax = axis_set[i] + + ax.grid(True) + if 'log' in LTR.flags: + ax.set_xscale('log') + for step_i in steps_data: + if LTR.axis: + x = LTR.get_axis(step_i) else: - axis_set = axis_set[i:i + 1] # Returns just one axis but enclosed in a list - magnitude = True - for ax in axis_set: - ax.grid(True) - if 'log' in LTR.flags: - ax.set_xscale('log') - for step_i in steps_data: - if LTR.axis: - x = LTR.get_axis(step_i) - else: - x = arange(LTR.nPoints) - y = traces[i].get_wave(step_i) - if 'complex' in LTR.flags: - x = mag(x) - if magnitude: - ax.set_yscale('log') - y = mag(y) - else: - y = angle(y, deg=True) - if write_labels: - ax.plot(x, y, label=str(steps_data[step_i])) - else: - ax.plot(x, y) - write_labels = False - + x = arange(LTR.nPoints) + y = traces[i].get_wave(step_i) + label = f"{trace.name}:{steps_data[step_i]})" if 'complex' in LTR.flags: - if magnitude: - title = f"{trace.name} Mag [db{what_to_units(trace.whattype)}]" - magnitude = False - else: - title = f"{trace.name} Phase [deg]" + x = mag(x) + ax.set_yscale('log') + y = mag(y) + ax.yaxis.label.set_color('blue') + ax.set(ylabel=label+'(dB)') + ax.plot(x, y) + ax_phase = ax.twinx() + y = phase_np(y, deg=True) + ax_phase.plot(x, y, color='red', linestyle='-.') + ax_phase.yaxis.label.set_color('red') + ax_phase.set(ylabel=label+'Phase (o)') + # title = f"{trace.name} Phase [deg]" + # ax.set_title(title) else: - title = f"{trace.name} [{what_to_units(trace.whattype)}]" - ax.set_title(title) - - plt.figlegend() + ax.plot(x, y) + ax.set(ylabel=label) + # title = f"{trace.name} [{what_to_units(trace.whattype)}]" + # ax.set_title(title) + fig.tight_layout() plt.show() ''' ''' diff --git a/PyLTSpice/simulator.py b/PyLTSpice/simulator.py deleted file mode 100644 index f93b4af..0000000 --- a/PyLTSpice/simulator.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env python -# coding=utf-8 - -# ------------------------------------------------------------------------------- -# ____ _ _____ ____ _ -# | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ -# | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ -# | __/| |_| | |___| | ___) | |_) | | (_| __/ -# |_| \__, |_____|_| |____/| .__/|_|\___\___| -# |___/ |_| -# -# Name: simulator.py -# Purpose: Represents a Simulator tool and it's command line options -# -# Author: Nuno Brum (nuno.brum@gmail.com) -# -# Created: 23-12-2016 -# Licence: refer to the LICENSE file -# ------------------------------------------------------------------------------- -import sys -import os -import subprocess -import time -from pathlib import Path - -if sys.version_info.major >= 3 and sys.version_info.minor >= 6: - clock_function = time.process_time - - - def run_function(command, timeout=None): - result = subprocess.run(command, timeout=timeout) - return result.returncode -else: - clock_function = time.clock - - - def run_function(command, timeout=None): - return subprocess.call(command, timeout=timeout) - - -class Simulator(object): - """Stores the simulator location and command line options and is responsible for generating netlists and running - simulations.""" - - ltspice_args = { - 'alt' : ['-alt'], # Set solver to Alternate. - 'ascii' : ['-ascii'], # Use ASCII.raw files. Seriously degrades program performance. - # 'batch': ['-b '], # Used by run command: Run in batch mode.E.g. "ltspice.exe-b deck.cir" will leave the data infile deck.raw - 'big' : ['-big'], # Start as a maximized window. - 'encrypt' : ['-encrypt'], - # Encrypt a model library.For 3rd parties wishing to allow people to use libraries without - # revealing implementation details. Not used by AnalogDevices models. - 'fastaccess': ['-FastAccess'], # Batch conversion of a binary.rawfile to Fast Access format. - 'FixUpSchematicFonts': ['-FixUpSchematicFonts'], - # Convert the font size field of very old user - authored schematic text to the modern default. - 'FixUpSymbolFonts': ['-FixUpSymbolFonts'], - # Convert the font size field of very old user - authored symbols to the modern default. - # See Changelog.txt for application hints. - - 'ini' : ['- ini', ''], # Specify an .ini file to use other than %APPDATA%\LTspice.ini - 'I' : ['-I'], # Specify a path to insert in the symbol and file search paths. - # Must be the last specified option. - # No space between "-I" and < path > is allowed. - 'max' : ['-max'], # Synonym for -big - 'netlist' : ['-netlist'], # Batch conversion of a schematic to a netlist. - 'norm' : ['-norm'], # Set solver to Normal. - 'PCBnetlist': ['-PCBnetlist'], # Batch conversion of a schematic to a PCB format netlist. - #'run' : ['-Run', '-b', '{path}'], # Start simulating the schematic opened on the command line without - # # pressing the Run button. - 'SOI' : ['-SOI'], # Allow MOSFET's to have up to 7 nodes even in subcircuit expansion. - 'sync' : ['-sync'], # Update component libraries - 'uninstall' : ['-uninstall'], # Please don't. Executes one step of the uninstallation process. - - } - - @classmethod - def get_default_simulator(cls): - if sys.platform == "linux": - if os.environ.get('LTSPICEFOLDER') is not None: - LTspice_exe = ["wine", os.environ['LTSPICEFOLDER'] + "/XVIIx64.exe"] - else: - LTspice_exe = ["wine", - os.path.expanduser("~") + "/.wine/drive_c/Program Files/LTC/LTspiceXVII/XVIIx64.exe"] - process_name = "XVIIx64.exe" - elif sys.platform == "darwin": - LTspice_exe = ['/Applications/LTspice.app/Contents/MacOS/LTspice'] - process_name = "XVIIx64" - else: # Windows - for exe in ( # Placed in order of preference. The first to be found will be used. - os.path.expanduser(r"~\AppData\Local\Programs\ADI\LTspice\LTspice.exe"), - r"C:\Program Files\ADI\LTspice\LTspice.exe", - r"C:\Program Files\LTC\LTspiceXVII\XVIIx64.exe", - r"C:\Program Files (x86)\LTC\LTspiceIV\scad3.exe", # Legacy LTspice IV - ): - if os.path.exists(exe): - print(f"Using LTspice installed in : '{exe}' ") - LTspice_exe = [exe] - _, process_name = os.path.split(exe) - break - else: - raise FileNotFoundError("A suitable exe file was not found. Please locate the spice simulator " - "executable and pass it to the SimCommander object by using the 'simulator'" - "parameter.") - return cls(LTspice_exe, process_name) - - @classmethod - def create_from(cls, path_to_exe): - """ - Creates a simulator class from a path to the simulator executable - :param path_to_exe: - :type path_to_exe: pathlib.Path or str - :return: a class instance representing the Spice simulator - :rtype: Simulator - """ - plib_path_to_exe = Path(path_to_exe) - if plib_path_to_exe.exists(): - if sys.platform == 'darwin': - process_name = plib_path_to_exe.stem - else: - process_name = plib_path_to_exe.name - return cls([plib_path_to_exe.as_posix()], process_name) - else: - raise FileNotFoundError(f"Provided exe file was not found '{path_to_exe}'") - - def __init__(self, spice_exe, process_name): - if not isinstance(spice_exe, list): - raise TypeError("spice_exe must be a list of strings that can be passed into the subprocess call") - - self.spice_exe = spice_exe - self.cmdline_switches = [] - self.process_name = process_name - - def clear_command_line_switches(self): - """Clear all the command line switches added previously""" - self.cmdline_switches.clear() - - def add_command_line_switch(self, switch, path=''): - """ - Adds a command line switch to the spice tool command line call. The following options are available for LTSpice: - - * 'alt' : Set solver to Alternate. - - * 'ascii' : Use ASCII.raw files. Seriously degrades program performance. - - * 'encrypt' : Encrypt a model library.For 3rd parties wishing to allow people to use libraries without - revealing implementation details. Not used by AnalogDevices models. - - * 'fastaccess': Batch conversion of a binary.rawfile to Fast Access format. - - * 'FixUpSchematicFonts' : Convert the font size field of very old user - authored schematic text to the - modern default. - - * 'FixUpSymbolFonts' : Convert the font size field of very old user - authored symbols to the modern - default. See Changelog.txt for application hints. - - * 'ini ' : Specify an .ini file to use other than %APPDATA%\LTspice.ini - - * 'I' : Specify a path to insert in the symbol and file search paths. Must be the last specified - option. - - * 'netlist' : Batch conversion of a schematic to a netlist. - - * 'normal' : Set solver to Normal. - - * 'PCBnetlist': Batch conversion of a schematic to a PCB format netlist. - - * 'SOI' : Allow MOSFET's to have up to 7 nodes even in subcircuit expansion. - - * 'sync' : Update component libraries - - * 'uninstall' : Executes one step of the uninstallation process. Please don't. - - - :param switch: switch to be added. If the switch is not on the list above, it should be correctly formatted with - the preceeding '-' switch - :type switch: - :param path: path to the file related to the switch being given. - :type path: str, optional - :return: - :rtype: - """ - if switch in self.ltspice_args: - switches = self.ltspice_args[switch] - switches = [switch.replace('{path}', path) for switch in switches] - self.cmdline_switches.extend(switches) - else: - self.cmdline_switches.append(switch) - if path is not None: - self.cmdline_switches.append(path) - - def run(self, netlist_file, timeout): - if sys.platform == 'darwin': - cmd_run = self.spice_exe + ['-b'] + [netlist_file] + self.cmdline_switches - else: - cmd_run = self.spice_exe + ['-Run'] + ['-b'] + [netlist_file] + self.cmdline_switches - # start execution - return run_function(cmd_run, timeout=timeout) - - def create_netlist(self, circuit_file): - # prepare instructions, two stages used to enable edits on the netlist w/o open GUI - # see: https://www.mikrocontroller.net/topic/480647?goto=5965300#5965300 - - if sys.platform == 'darwin': - NotImplementedError("In this platform LTSpice doesn't have netlist generation capabilities") - cmd_netlist = self.spice_exe + ['-netlist'] + [circuit_file] - return run_function(cmd_netlist) - - def kill_all(self): - import psutil - for proc in psutil.process_iter(): - # check whether the process name matches - - if proc.name() == self.process_name: - print("killing ltspice", proc.pid) - proc.kill() diff --git a/tests/batch_test3.py b/tests/batch_test3.py index 8f7cc8d..9a265cc 100644 --- a/tests/batch_test3.py +++ b/tests/batch_test3.py @@ -1,4 +1,4 @@ -from PyLTSpice import SimRunner, SpiceEditor, LTspice +from PyLTSpice import SimRunner, SpiceEditor from time import sleep from random import random @@ -12,7 +12,7 @@ def processing_data(raw_file, log_file): return "This is the result passed to the iterator" -runner = SimRunner(output_folder='./temp_batch3', simulator=LTspice) # Configures the simulator to use and output +runner = SimRunner(output_folder='./temp_batch3') # Configures the simulator to use and output # folder netlist = SpiceEditor("Batch_Test.asc") # Open the Spice Model, and creates the .net diff --git a/tests/batch_test4.py b/tests/batch_test4.py index 51ac1a1..b26469b 100644 --- a/tests/batch_test4.py +++ b/tests/batch_test4.py @@ -1,5 +1,5 @@ -from PyLTSpice import SimRunner, SpiceEditor, LTspice +from PyLTSpice import SimRunner, SpiceEditor from PyLTSpice.sim.process_callback import ProcessCallback # Importing the ProcessCallback class type @@ -10,11 +10,11 @@ class CallbackProc(ProcessCallback): def callback(raw_file, log_file): print("Handling the simulation data of ""%s"", log file ""%s""" % (raw_file, log_file)) # Doing some processing here - return "Parsed Result" + return "Parsed Result of ""%s""" % raw_file, ", log file ""%s""" % log_file if __name__ == "__main__": - + from PyLTSpice.sim.ltspice_simulator import LTspice runner = SimRunner(output_folder='./temp_batch4', simulator=LTspice) # Configures the simulator to use and output # folder diff --git a/tests/fra_example.py b/tests/fra_example.py index 09ad37e..f2ca515 100644 --- a/tests/fra_example.py +++ b/tests/fra_example.py @@ -24,7 +24,7 @@ def plot_fra(name, freq, complex_data, FRA_data ): ax2 = ax1.twinx() gain = 20*np.log10(np_mag(complex_data)) - phase = np_angle( complex_data, deg=True) + phase = np_angle(complex_data, deg=True) for i in range(len(phase)): if phase[i] > 0: phase[i] = phase[i] - 360 From 685115bfdbb19df1e213291ad920385bd84c8812 Mon Sep 17 00:00:00 2001 From: nunobrum Date: Sun, 14 May 2023 21:14:19 +0200 Subject: [PATCH 2/2] Cleanup * Deleting doc_build files * deleting _pycache_ --- doc_build/html/.buildinfo | 4 - doc_build/html/Histogram.html | 148 --- doc_build/html/LTSteps.html | 433 ------- doc_build/html/SemiOpLogReader.html | 177 --- doc_build/html/SpiceEditor.html | 669 ---------- .../html/_modules/PyLTSpice/LTSteps.html | 839 ------------ .../_modules/PyLTSpice/SemiDevOpReader.html | 226 ---- .../html/_modules/PyLTSpice/SpiceEditor.html | 1123 ----------------- .../html/_modules/PyLTSpice/raw_read.html | 854 ------------- .../html/_modules/PyLTSpice/raw_write.html | 563 --------- .../html/_modules/PyLTSpice/sim_batch.html | 588 --------- doc_build/html/_modules/index.html | 117 -- doc_build/html/_sources/index.rst.txt | 29 - doc_build/html/_static/basic.css | 855 ------------- doc_build/html/_static/doctools.js | 315 ----- .../html/_static/documentation_options.js | 12 - doc_build/html/_static/file.png | Bin 286 -> 0 bytes doc_build/html/_static/language_data.js | 297 ----- doc_build/html/_static/minus.png | Bin 90 -> 0 bytes doc_build/html/_static/plus.png | Bin 90 -> 0 bytes doc_build/html/_static/pygments.css | 77 -- doc_build/html/_static/searchtools.js | 514 -------- doc_build/html/genindex.html | 644 ---------- doc_build/html/index.html | 201 --- doc_build/html/modules.html | 137 -- doc_build/html/objects.inv | Bin 1638 -> 0 bytes doc_build/html/py-modindex.html | 133 -- doc_build/html/raw_read.html | 690 ---------- doc_build/html/raw_write.html | 224 ---- doc_build/html/search.html | 128 -- doc_build/html/searchindex.js | 5 - doc_build/html/sim_batch.html | 312 ----- 32 files changed, 10314 deletions(-) delete mode 100644 doc_build/html/.buildinfo delete mode 100644 doc_build/html/Histogram.html delete mode 100644 doc_build/html/LTSteps.html delete mode 100644 doc_build/html/SemiOpLogReader.html delete mode 100644 doc_build/html/SpiceEditor.html delete mode 100644 doc_build/html/_modules/PyLTSpice/LTSteps.html delete mode 100644 doc_build/html/_modules/PyLTSpice/SemiDevOpReader.html delete mode 100644 doc_build/html/_modules/PyLTSpice/SpiceEditor.html delete mode 100644 doc_build/html/_modules/PyLTSpice/raw_read.html delete mode 100644 doc_build/html/_modules/PyLTSpice/raw_write.html delete mode 100644 doc_build/html/_modules/PyLTSpice/sim_batch.html delete mode 100644 doc_build/html/_modules/index.html delete mode 100644 doc_build/html/_sources/index.rst.txt delete mode 100644 doc_build/html/_static/basic.css delete mode 100644 doc_build/html/_static/doctools.js delete mode 100644 doc_build/html/_static/documentation_options.js delete mode 100644 doc_build/html/_static/file.png delete mode 100644 doc_build/html/_static/language_data.js delete mode 100644 doc_build/html/_static/minus.png delete mode 100644 doc_build/html/_static/plus.png delete mode 100644 doc_build/html/_static/pygments.css delete mode 100644 doc_build/html/_static/searchtools.js delete mode 100644 doc_build/html/genindex.html delete mode 100644 doc_build/html/index.html delete mode 100644 doc_build/html/modules.html delete mode 100644 doc_build/html/objects.inv delete mode 100644 doc_build/html/py-modindex.html delete mode 100644 doc_build/html/raw_read.html delete mode 100644 doc_build/html/raw_write.html delete mode 100644 doc_build/html/search.html delete mode 100644 doc_build/html/searchindex.js delete mode 100644 doc_build/html/sim_batch.html diff --git a/doc_build/html/.buildinfo b/doc_build/html/.buildinfo deleted file mode 100644 index c764058..0000000 --- a/doc_build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 8fe1b54772906365eb763524daca3e42 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/doc_build/html/Histogram.html b/doc_build/html/Histogram.html deleted file mode 100644 index 6aa1fae..0000000 --- a/doc_build/html/Histogram.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - - - Histogram.py — PyLTSpice 3.0 documentation - - - - - - - - - - - - - - -
-
- -
- -
-
-
- -
-

Histogram.py

-

This module uses matplotlib to plot an histogram of a gaussian distribution and calculates the project n-sigma interval.

-

The data can either collected from the clipboard or from a text file. Use the following command line text to call -this module.

-
python -m PyLTSpice.Histogram [options] [data_file] TRACE
-
-
-

The help can be obtained by calling the script without arguments

-
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'
-
-
-
- - -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/LTSteps.html b/doc_build/html/LTSteps.html deleted file mode 100644 index ed38f71..0000000 --- a/doc_build/html/LTSteps.html +++ /dev/null @@ -1,433 +0,0 @@ - - - - - - - - LTSteps.py — PyLTSpice 3.0 documentation - - - - - - - - - - - - - - -
-
- -
- -
-
-
- -
-

LTSteps.py

-

This module allows to process data generated by LTSpice during simulation. There are three types of files that are -handled by this module.

-
-
    -
  • log files - Files with the extension ‘.log’ that are automatically generated during simulation, and that are -normally accessible with the shortcut Ctrl+L after a simulation is ran.Log files are interesting for two reasons.

    -
    -

    1. If .STEP primitives are used, the log file contain the correspondence between the step run and the step -value configuration.

    -

    2. If .MEAS primitives are used in the schematic, the log file contains the measurements made on the output -data.

    -
    -

    LTSteps.py can be used to retrieve both step and measurement information from log files.

    -
  • -
  • txt files - Files exported from the Plot File -> Export data as text menu. This file is an text file where data is -saved in the text format. The reason to use PyLTSpice instead of another popular lib as pandas, is because the data -format when .STEPS are used in the simulation is not not very practical. The PyLTSpice LTSteps.py can be used to -reformat the text, so that the run parameter is added to the data as an additional column instead of a table -divider. Please Check LTSpiceExport class for more information.

  • -
  • mout files - Files generated by the Plot File -> Execute .MEAS Script menu. This command allows the user to run -predefined .MEAS commands which create a .mout file. A .mout file has the measurement information stored in the -following format:

    -
    Measurement: Vout_rms
    -step        RMS(V(OUT))     FROM    TO
    - 1  1.41109 0       0.001
    - 2  1.40729 0       0.001
    -
    -Measurement: Vin_rms
    -  step      RMS(V(IN))      FROM    TO
    -     1      0.706221        0       0.001
    -     2      0.704738        0       0.001
    -
    -Measurement: gain
    -  step      Vout_rms/Vin_rms
    -     1      1.99809
    -     2      1.99689
    -
    -
    -
  • -
-
-

The LTSteps.py can be used directly from a command line by invoking python with the -m option as exemplified below.

-
$ python -m PyLTSpice.LTSteps <path_to_filename>
-
-
-

If <path_to_filename> is a log file, it will create a file with the same name, but with extension .tout that is a -tab separated value (tsv) file, which contains the .STEP and .MEAS information collected.

-

If <path_to_filename> is a txt exported file, it will create a file with the same name, but with extension .tsv a -tab separated value (tsv) file, which contains data reformatted with the step number as one of the columns. Please -consult the reformat_LTSpice_export() function for more information.

-

If <path_to_filename> is a mout file, it will create a file with the same name, but with extension .tmout that is a -tab separated value (tsv) file, which contains the .MEAS information collected, but adding the STEP run information -as one of the columns.

-

If <path_to_filename> argument is ommited, the script will automatically search for the newest .log/.txt/.mout file -and use it.

-
-
-class PyLTSpice.LTSteps.LTComplex(strvalue)[source]
-

Bases: object

-

Class to represent complex numbers as exported by LTSpice

-
-
-complex_match = re.compile('\\((?P<mag>[^dB]*)(dB)?,(?P<ph>.*)°\\)')
-
- -
-
-to_complex()[source]
-
- -
- -
-
-class PyLTSpice.LTSteps.LTSpiceExport(export_filename: str)[source]
-

Bases: object

-

Opens and reads LTSpice export data when using the “Export data as text” in the File Menu on the waveform window.

-

The data is then accessible by using the following attributes implemented in this class.

-
-
Property headers
-

list containing the headers on the exported data

-
-
Property dataset
-

dictionary in which the keys are the the headers and the export file and the values are -lists. When reading STEPed data, a new key called ‘runno’ is added to the dataset.

-
-
-

Examples

-
export_data = LTSpiceExport("export_data_file.txt")
-for value in export_data.dataset['I(V1)']:
-    print(f"Do something with this value {value}")
-
-
-
-
Parameters
-

export_filename (str) – path to the Export file.

-
-
-
- -
-
-class PyLTSpice.LTSteps.LTSpiceLogReader(log_filename: str, read_measures=True, step_set={}, encoding=None)[source]
-

Bases: object

-

Reads an LTSpice log file and retrieves the step information if it exists. The step information is then accessible -by using the ‘stepset’ property of this class. -This class is intended to be used together with the LTSpice_RawRead to retrieve the runs that are associated with a -given parameter setting.

-

This class constructor only reads the step information of the log file. If the measures are needed, then the user -should call the get_measures() method.

-
-
Property stepset
-

dictionary in which the keys are the variables that were STEP’ed during the simulation and -the associated value is a list representing the sequence of assigned values during simulation.

-
-
Property headers
-

list containing the headers on the exported data. This is only populated when the read_measures -optional parameter is set to False.

-
-
Property dataset
-

dictionary in which the keys are the the headers and the export file and the values are -lists. This is information is only populated when the read_measures optional parameter is set to False.

-
-
Parameters
-
    -
  • log_filename (str) – path to the Export file.

  • -
  • read_measures (boolean) – Optional parameter to skip measuring data reading.

  • -
  • step_set (dict) – Optional parameter to provide the steps from another file. This is used to process .mout files.

  • -
-
-
-
-
-export_data(export_file: str, append_with_line_prefix=None)[source]
-

Exports the measurement information to a tab separated value (.tsv) format. If step data is found, it is -included in the exported file.

-

When using export data together with sim_batch.py classes, it may be helpful to append data to an existing -file. For this purpose, the user can user the append_with_line_prefix argument to indicate that an append should -be done. And in this case, the user must provide a string that will identify the LTSpice batch run.

-
-
Parameters
-
    -
  • export_file (str) – path to the file containing the information

  • -
  • append_with_line_prefix (str) – user information to be written in the file in case an append is to be made.

  • -
-
-
Returns
-

Nothing

-
-
-
- -
-
-get_measure_names() → List[str][source]
-

Returns the names of the measurements read from the log file. -:return: List of measurement names. -:rtype: list of str

-
- -
-
-get_measure_value(measure: str, step: int = None) → Union[float, int, str, complex][source]
-

Returns a measure value on a given step.

-
-
Parameters
-
    -
  • measure (str) – name of the measurement to get

  • -
  • step (int) – optional step number if the simulation has no steps.

  • -
-
-
-
- -
-
-get_measure_values_at_steps(measure: str, steps: Union[int, Iterable])[source]
-

Returns the measurements taken at a list of steps provided by the steps list.

-
-
Parameters
-
    -
  • measure (str) – name of the measurement to get.

  • -
  • steps (int or list) – step number, or list of step numbers.

  • -
-
-
Returns
-

measurement or list of measurements

-
-
Return type
-

int or Iterable

-
-
-
- -
-
-get_step_vars() → List[str][source]
-

Returns the stepped variable names of . -:return: List of step variables. -:rtype: list of str

-
- -
-
-split_complex_values_on_datasets()[source]
-

Internal function to split the complex values into additional two columns -TODO: Delete the old data and insert new ones the the right position

-
- -
-
-steps_with_conditions(**conditions) → List[int][source]
-

Returns the steps that respect one more more equality conditions

-
-
Key conditions
-

parameters within the LTSpice simulation. Values are the matches to be found.

-
-
Returns
-

List of steps that repect all the given conditions

-
-
Return type
-

List[int]

-
-
-
- -
-
-steps_with_parameter_equal_to(param: str, value: Union[str, int, float]) → List[int][source]
-

Returns the steps that contain a given condition.

-
-
Parameters
-
    -
  • param (str) – parameter identifier on LTSpice simulation

  • -
  • value

  • -
-
-
Returns
-

List of positions that respect the condition of equality with parameter value

-
-
Return type
-

List[int]

-
-
-
- -
- -
-
-PyLTSpice.LTSteps.message(*strs)[source]
-
- -
-
-PyLTSpice.LTSteps.reformat_LTSpice_export(export_file: str, tabular_file: str)[source]
-

Reads an LTSpice File Export file and writes it back in a format that is more convenient for data treatment.

-

When using the “Export data as text” in the raw file menu the data is already exported in a tabular format. -However, if steps are being used, the step information doesn’t appear on the table. Instead the successive STEP -runs are stacked on one after another, separated by the following text:

-
Step Information: Ton=400m  (Run: 2/2)
-
-
-

What would be desirable would be that the step number (Run number) and the STEP variable would be placed within the -columns. This allows, for example, using Excel functionality known as Pivot Tables to filter out data, or some other -database selection function. -The tab is chosen as separator because it is normally compatible with pasting data into Excel.

-
-
Parameters
-
    -
  • export_file (str) – Filename of the .txt file generated by the “Export Data as Text”

  • -
  • tabular_file (str) – Filename of the tab separated values (TSV) file that

  • -
-
-
Returns
-

Nothing

-
-
Return type
-

None

-
-
-
- -
-
-PyLTSpice.LTSteps.try_convert_value(value: Union[str, int, float]) → Union[int, float, str][source]
-

Tries to convert the string into an integer and if it fails, tries to convert to a float, if it fails, then returns the -value as string.

-
-
Parameters
-

value (str, int or float) – value to convert

-
-
Returns
-

converted value, if applicable

-
-
Return type
-

int, float, str

-
-
-
- -
-
-PyLTSpice.LTSteps.try_convert_values(values: Iterable[str]) → List[Union[int, float, str]][source]
-

Same as try_convert_values but applicable to an iterable

-
-
Parameters
-

values – Iterable that returns strings

-
-
Returns
-

list with the values converted to either integer (int) or floating point (float)

-
-
Return type
-

List[str]

-
-
-
- -
- - -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/SemiOpLogReader.html b/doc_build/html/SemiOpLogReader.html deleted file mode 100644 index 96a9cec..0000000 --- a/doc_build/html/SemiOpLogReader.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - LTSpice_SemiDevOpReader.py — PyLTSpice 3.0 documentation - - - - - - - - - - - - - -
-
- -
- -
-
-
- -
-

LTSpice_SemiDevOpReader.py

-

Implements a parser for extracting Semiconductor Devices Operating Points from an LTSpice log file.

-
-
-PyLTSpice.SemiDevOpReader.opLogReader(filename: str) → dict[source]
-

This function is exclusively dedicated to retrieving operation point parameters of Semiconductor Devices. This is -handled separately from the main LogReader class because of its specialization and therefore not judged to be -of interest to the typical LTSpice user making board level simulations.

-

Below is an excerpt of a Semiconductor Device Operating Points log.py

-
Semiconductor Device Operating Points:
-
-                        --- Diodes ---
-Name:         d:m6:1:para2:1               d:m4:1:para2:1               d:m3:1:para2:1         d:m6:1:para1:2
-Model: m6:1:para2:dpar_5v_psubnburd m4:1:para2:dpar_5v_psubnburd m3:1:para2:dpar_5v_psubnburd dpar_pburdnburdsw
-Id:              3.45e-19                     3.45e-19                     3.45e-19                1.11e-13
-Vd:              3.27e-07                     3.27e-07                     3.27e-07                9.27e-02
-...
-CAP:             3.15e-14                     3.15e-14                     3.15e-14                5.20e-14
-
-                    --- Bipolar Transistors ---
-Name:     q:q2:1:2    q:q2:1:1     q:q1:1:2    q:q1:1:1     q:q7:3
-Model:  q2:1:qnl_pnp q2:1:qnl_m  q1:1:qnl_pnp q1:1:qnl_m   qpl_parc
-Ib:       3.94e-12     4.69e-08    7.43e-13     4.70e-08    3.75e-12
-Ic:      -2.34e-12     4.57e-06   -7.44e-13     4.50e-06   -2.35e-12
-Vbe:      1.60e+00     7.40e-01   -7.88e-04     7.40e-01    1.40e+00
-
-
-

This function will parse the log file and will produce a dictionary that contains all the information retrieved with -the following format:

-
semi_ops = {
-    'Diodes': {
-        'd:m6:1:para2:1': {
-            'Model': 'm6:1:para2:dpar_5v_psubnburd', 'Id': 3.45e-19, 'Vd': 3.27e-07, ... 'CAP': 3.15e-14 },
-        'd:m4:1:para2:1': {
-            'Model': 'm4:1:para2:dpar_5v_psubnburd', 'Id': 3.45e-19, 'Vd': 3.27e-07, ..., 'CAP': 3.15e-14 },
-        'd:m3:1:para2:1': {
-            'Model': 'm3:1:para2:dpar_5v_psubnburd', 'Id': 3.45e-19, 'Vd': 3.27e-07, ..., 'CAP': 3.15e-14 },
-        'd:m6:1:para1:2': {
-            'Model': 'dpar_pburdnburdsw', 'Id': 1.11e-13, 'Vd': 0.0927, ..., 'CAP': 5.2e-14 },
-    },
-    'Bipolar Transistors': {
-        'q:q2:1:2': {
-            'Model': 'q2:1:qnl_pnp', 'Ib': 3.94e-12, 'Ic': -2.34e-12, 'Vbe': 160, ... },
-        'q:q2:1:1': {
-            'Model': 'q2:1:qnl_m', 'Ib': 4.69e-08, 'Ic': 4.57e-06, 'Vbe': 0.074, ... },
-        'q:q1:1:2': {
-            'Model': 'q1:1:qnl_pnp', 'Ib': 7.43e-13, 'Ic': -7.44e-13, 'Vbe': -0.000788, ... },
-        'q:q1:1:1': {
-            'Model': 'q1:1:qnl_m', 'Ib': 4.7e-08, 'Ic': 4.5e-06, 'Vbe': 0.74, ... },
-        'q:q7:3': {
-            'Model': 'qpl_parc', 'Ib': 3.75e-12, 'Ic': -2.35e-12, 'Vbe': 1.4, ... },
-    },
-}
-
-
-
-
Parameters
-

filename (str) – path to the log file containing the Semiconductor Device Operating Points

-
-
Returns
-

Dictionary containing the information as described above.

-
-
Return type
-

dict

-
-
-
- -
- - -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/SpiceEditor.html b/doc_build/html/SpiceEditor.html deleted file mode 100644 index 2355183..0000000 --- a/doc_build/html/SpiceEditor.html +++ /dev/null @@ -1,669 +0,0 @@ - - - - - - - - SpiceEditor.py — PyLTSpice 3.0 documentation - - - - - - - - - - - - - - -
-
- -
- -
-
-
- -
-

SpiceEditor.py

-
-
-exception PyLTSpice.SpiceEditor.ComponentNotFoundError[source]
-

Bases: Exception

-

Component Not Found Error

-
- -
-
-PyLTSpice.SpiceEditor.END_LINE_TERM = '\n'
-

This controls the end of line terminator used

-
- -
-
-exception PyLTSpice.SpiceEditor.ParameterNotFoundError(parameter)[source]
-

Bases: Exception

-

ParameterNotFound Error

-
- -
-
-PyLTSpice.SpiceEditor.SUBCIRCUIT_DIVIDER = ':'
-

This controls the Subcircuit divider when setting component values inside subcircuits.

-
- -
-
-class PyLTSpice.SpiceEditor.SpiceCircuit[source]
-

Bases: object

-

The Spice Circuit represents subcircuits within a SPICE circuit and since subcircuits can have subcircuits inside -them, it serves as base for the top level netlist. -This hierchical approach helps to encapsulate and protect parameters and components from a edits made a a higher -level. -The information is stored in a python list, each line of the SPICE netlist is an item of the list. A Subcircuit -is represented as a SpiceCircuit object.

-
-
-static add_library_search_paths(paths)[source]
-

Adding search paths for libraries. By default the local directory and the -~username/”Documents/LTspiceXVII/lib/sub will be searched forehand. Only when a library is not found in these -paths then the paths added by this method will be searched. -Alternatively PyLTSpice.SpiceEditor.LibSearchPaths.append(paths) can be used.”

-
-
Parameters
-

paths (str) – Path to add to the Search path

-
-
Returns
-

Nothing

-
-
Return type
-

None

-
-
-
- -
-
-clone(**kwargs)PyLTSpice.SpiceEditor.SpiceCircuit[source]
-

Creates a new copy of the SpiceCircuit. Change done at the new copy are not affecting the original

-
-
Key new_name
-

The new name to be given to the circuit

-
-
Returns
-

The new replica of the SpiceCircuit object

-
-
Return type
-

SpiceCircuit

-
-
-
- -
-
-get_all_nodes() → List[str][source]
-

A function that retrieves all nodes existing on a Netlist

-
-
Returns
-

Circuit Nodes

-
-
Return type
-

list[str]

-
-
-
- -
-
-get_component_floatvalue(element: str) → str[source]
-

Returns the value of a component retrieved from the netlist.

-
-
Parameters
-

element (str) – Reference of the circuit element to get the value in float format.

-
-
Returns
-

value of the circuit element in float type

-
-
Return type
-

float

-
-
Raises
-

ComponentNotFoundError - In case the component is not found

-

NotImplementedError - for not supported operations

-
-
-
- -
-
-get_component_info(component) → dict[source]
-

Retrieves the component information as defined in the corresponding REGEX. The line number is also added.

-
-
Parameters
-

component (str) – Reference of the component

-
-
Returns
-

Dictionary with the component information

-
-
Return type
-

dict

-
-
Raises
-

UnrecognizedSyntaxError when the line doesn’t match the expected REGEX. NotImplementedError of there -isn’t an associated regular expression for the component prefix.

-
-
-
- -
-
-get_component_nodes(element: str) → List[str][source]
-

Returns the nodes to which the component is attached to.

-
-
Parameters
-

element (str) – Reference of the circuit element to get the nodes.

-
-
Returns
-

List of nodes

-
-
Return type
-

list

-
-
-
- -
-
-get_component_value(element: str) → str[source]
-

Returns the value of a component retrieved from the netlist.

-
-
Parameters
-

element (str) – Reference of the circuit element to get the value.

-
-
Returns
-

value of the circuit element .

-
-
Return type
-

str

-
-
Raises
-

ComponentNotFoundError - In case the component is not found

-

NotImplementedError - for not supported operations

-
-
-
- -
-
-get_components(prefixes='*') → list[source]
-

Returns a list of components that match the list of prefixes indicated on the parameter prefixes. -In case prefixes is left empty, it returns all the ones that are defined by the REPLACE_REGEXES. -The list will contain the designators of all components found.

-
-
Parameters
-

prefixes (str) – Type of prefixes to search for. Examples: ‘C’ for capacitors; ‘R’ for Resistors; etc… See prefixes -in SPICE documentation for more details. -The default prefix is ‘*’ which is a special case that returns all components.

-
-
Returns
-

A list of components matching the prefixes demanded.

-
-
-
- -
-
-get_parameter(param: str) → str[source]
-

Retrieves a Parameter from the Netlist

-
-
Parameters
-

param (str) – Name of the parameter to be retrieved

-
-
Returns
-

Value of the parameter being sought

-
-
Return type
-

str

-
-
Raises
-

ParameterNotFoundError - In case the component is not found

-
-
-
- -
-
-name()[source]
-
- -
-
-remove_component(designator: str)[source]
-

Removes a component from the design. -Note: Current implemetation only alows removal of a component from the main netlist, not from a sub-circuit.

-
-
Parameters
-

designator (str) – Component reference in the design. Ex: V1, C1, R1, etc…

-
-
Returns
-

Nothing

-
-
Raises
-

ComponentNotFoundError - When the component doesn’t exist on the netlist.

-
-
-
- -
-
-set_component_value(device: str, value: Union[str, int, float]) → None[source]
-

Changes the value of a component, such as a Resistor, Capacitor or Inductor. For components inside -subcircuits, use the subcirciut designator prefix with ‘:’ as separator (Example X1:R1) -Usage:

-
LTC.set_component_value('R1', '3.3k')
-LTC.set_component_value('X1:C1', '10u')
-
-
-
-
Parameters
-
    -
  • device (str) – Reference of the circuit element to be updated.

  • -
  • value (str, int or float) – value to be be set on the given circuit element. Float and integer values will automatically -formatted as per the engineering notations ‘k’ for kilo, ‘m’, for mili and so on.

  • -
-
-
Raises
-

ComponentNotFoundError - In case the component is not found

-

ValueError - In case the value doesn’t correspond to the expected format

-

NotImplementedError - In case the circuit element is defined in a format which is not supported by this -version.

-

If this is the case, use GitHub to start a ticket. https://github.com/nunobrum/PyLTSpice

-
-
-
- -
-
-set_component_values(**kwargs)[source]
-

Adds one or more components on the netlist. The argument is in the form of a key-value pair where each -component designator is the key and the value is value to be set in the netlist.

-

Usage 1:

-
LTC.set_component_values(R1=330, R2="3.3k", R3="1Meg", V1="PWL(0 1 30m 1 30.001m 0 60m 0 60.001m 1)")
-
-
-

Usage 2:

-
value_settings = {'R1': 330, 'R2': '3.3k', 'R3': "1Meg", 'V1': 'PWL(0 1 30m 1 30.001m 0 60m 0 60.001m 1)'}
-LTC.set_component_values(**value_settings)
-
-
-
-
Key <comp_ref>
-

The key is the component designator (Ex: V1) and the value is the value to be set. Values can either be -strings; integers or floats

-
-
Returns
-

Nothing

-
-
Raises
-

ComponentNotFoundError - In case one of the component is not found.

-
-
-
- -
-
-set_element_model(element: str, model: str) → None[source]
-

Changes the value of a circuit element, such as a diode model or a voltage supply. -Usage:

-
LTC.set_element_model('D1', '1N4148')
-LTC.set_element_model('V1' "SINE(0 1 3k 0 0 0)")
-
-
-
-
Parameters
-
    -
  • element (str) – Reference of the circuit element to be updated.

  • -
  • model (str) – model name of the device to be updated

  • -
-
-
Raises
-

ComponentNotFoundError - In case the component is not found

-

ValueError - In case the model format contains irregular characters

-

NotImplementedError - In case the circuit element is defined in a format which is not supported by this version.

-

If this is the case, use GitHub to start a ticket. https://github.com/nunobrum/PyLTSpice

-
-
-
- -
-
-set_parameter(param: str, value: Union[str, int, float]) → None[source]
-

Adds a parameter to the SPICE netlist.

-

Usage:

-
LTC.set_parameter("TEMP", 80)
-
-
-

This adds onto the netlist the following line:

-
.PARAM TEMP=80
-
-
-

This is an alternative to the set_parameters which is more pythonic in it’s usage, -and allows setting more than one parameter at once.

-
-
Parameters
-
    -
  • param (str) – Spice Parameter name to be added or updated.

  • -
  • value (str, int or float) – Parameter Value to be set.

  • -
-
-
Returns
-

Nothing

-
-
-
- -
-
-set_parameters(**kwargs)[source]
-

Adds one or more parameters to the netlist. -Usage:

-
for temp in (-40, 25, 125):
-    for freq in sweep_log(1, 100E3,):
-        LTC.set_parameters(TEMP=80, freq=freq)
-
-
-
-
Key param_name
-

Key is the parameter to be set. values the ther corresponding values. Values can either be a str; an int or -a float.

-
-
Returns
-

Nothing

-
-
-
- -
-
-setname(new_name: str)[source]
-

Renames the subcircuit to a new name. No check is done to the new game give. It is up to the user to make sure -that the new name is valid.

-
-
Parameters
-

new_name (str) – The new Name.

-
-
Returns
-

Nothing

-
-
Return type
-

None

-
-
-
- -
- -
-
-class PyLTSpice.SpiceEditor.SpiceEditor(netlist_file, encoding='autodetect')[source]
-

Bases: PyLTSpice.SpiceEditor.SpiceCircuit

-

This class implements interfaces to manipulate SPICE netlist files. The class doesn’t update the netlist file -itself. After implementing the modifications the user should call the “write_netlist” method to write a new -netlist file. -:param netlist_file: Name of the .NET file to parse -:type netlist_file: str -:param encoding: Forcing the encoding to be used on the circuit netlile read. Defaults to ‘autodetect’ which will -call a function that tries to detect the encoding automatically. This however is not 100% fool proof. -:type encoding: str, optional

-
-
-add_instruction(instruction: str) → None[source]
-

Serves to add SPICE instructions to the simulation netlist. For example:

-
.tran 10m ; makes a transient simulation
-.meas TRAN Icurr AVG I(Rs1) TRIG time=1.5ms TARG time=2.5ms" ; Establishes a measuring
-.step run 1 100, 1 ; makes the simulation run 100 times
-
-
-
-
Parameters
-

instruction (str) – Spice instruction to add to the netlist. This instruction will be added at the end of the netlist, -typically just before the .BACKANNO statement

-
-
Returns
-

Nothing

-
-
-
- -
-
-add_instructions(*instructions) → None[source]
-

Adds a list of instructions to the SPICE NETLIST. -Example:

-
LTC.add_instructions(
-    ".STEP run -1 1023 1",
-    ".dc V1 -5 5"
-)
-
-
-
-
Parameters
-

instructions (list) – Argument list of instructions to add

-
-
Returns
-

Nothing

-
-
-
- -
-
-static find_subckt_in_lib(library, subckt_name)PyLTSpice.SpiceEditor.SpiceEditor[source]
-

Finds returns a Subckt from a library file

-
-
Parameters
-
    -
  • library (str) – path to the library to search

  • -
  • subckt_name – Subcircuit to search for

  • -
-
-
-
- -
-
-remove_instruction(instruction) → None[source]
-

Usage a previously added instructions. -Example:

-
LTC.remove_instruction(".STEP run -1 1023 1")
-
-
-

:param instructions The list of instructions to remove. Each instruction is of the type ‘str’ -:type instruction: str -:returns: Nothing -TODO: This only works with a full line instruction. Make it more inteligent so it recognizes .models, .param -and .subckt

-
- -
-
-reset_netlist() → None[source]
-

Removes all previous edits done to the netlist, i.e. resets it to the original state.

-
-
Returns
-

Nothing

-
-
-
- -
-
-write_netlist(run_netlist_file: str) → None[source]
-

Writes the netlist will all the requested updates into a file named <run_netlist_file>. -:param run_netlist_file: File name of the netlist file. -:type run_netlist_file: str -:return Nothing

-
- -
- -
-
-exception PyLTSpice.SpiceEditor.UnrecognizedSyntaxError(line, regex)[source]
-

Bases: Exception

-

Line doesn’t match expected Spice syntax

-
- -
-
-PyLTSpice.SpiceEditor.format_eng(value) → str[source]
-

Helper function for formating value with the SI qualifiers. That is, it will use

-
-
    -
  • p for pico (10E-12)

  • -
  • n for nano (10E-9)

  • -
  • u for micro (10E-6)

  • -
  • m for mili (10E-3)

  • -
  • k for kilo (10E+3)

  • -
  • Meg for Mega (10E+6)

  • -
-
-
-
Parameters
-

value (float) – float value to format

-
-
Returns
-

String wiht the formatted value

-
-
Return type
-

str

-
-
-
- -
-
-PyLTSpice.SpiceEditor.get_line_command(line) → str[source]
-

Retrives the type of SPICE command in the line. -Starts by removing the leading spaces and the evaluates if it is a comment, a directive or a component.

-
- -
-
-PyLTSpice.SpiceEditor.scan_eng(value: str) → float[source]
-

Converts a string to a float, considering SI multipliers

-
-
    -
  • f for femto (10E-15)

  • -
  • p for pico (10E-12)

  • -
  • n for nano (10E-9)

  • -
  • u or µ for micro (10E-6)

  • -
  • m for mili (10E-3)

  • -
  • k for kilo (10E+3)

  • -
  • Meg for Mega (10E+6)

  • -
-
-

The extra unit qualifiers such as V for volts or F for Farads are ignored.

-
-
Parameters
-

value (str) – string to be converted to float

-
-
Returns
-

-
-
Return type
-

float

-
-
Raises
-

ValueError when the value cannot be converted.

-
-
-
- -
- - -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/_modules/PyLTSpice/LTSteps.html b/doc_build/html/_modules/PyLTSpice/LTSteps.html deleted file mode 100644 index 446e39b..0000000 --- a/doc_build/html/_modules/PyLTSpice/LTSteps.html +++ /dev/null @@ -1,839 +0,0 @@ - - - - - - - - PyLTSpice.LTSteps — PyLTSpice 3.0 documentation - - - - - - - - - - - - -
-
- -
- -
-
-
- -

Source code for PyLTSpice.LTSteps

-#!/usr/bin/env python
-# coding=utf-8
-
-# -------------------------------------------------------------------------------
-# Name:        LTSteps.py
-# Purpose:     Process LTSpice output files and align data for usage in a spread-
-#              sheet tool such as Excel, or Calc.
-#
-# Author:      Nuno Brum (nuno.brum@gmail.com)
-#
-# Created:     19-05-2014
-# Licence:     refer to the LICENSE file
-# -------------------------------------------------------------------------------
-
-"""
-This module allows to process data generated by LTSpice during simulation. There are three types of files that are
-handled by this module.
-
-    + log files - Files with the extension '.log' that are automatically generated during simulation, and that are
-      normally accessible with the shortcut Ctrl+L after a simulation is ran.Log files are interesting for two reasons.
-
-            1. If .STEP primitives are used, the log file contain the correspondence between the step run and the step
-            value configuration.
-
-            2. If .MEAS primitives are used in the schematic, the log file contains the measurements made on the output
-            data.
-
-      LTSteps.py can be used to retrieve both step and measurement information from log files.
-
-    + txt files - Files exported from the Plot File -> Export data as text menu. This file is an text file where data is
-      saved in the text format. The reason to use PyLTSpice instead of another popular lib as pandas, is because the data
-      format when .STEPS are used in the simulation is not not very practical. The PyLTSpice LTSteps.py can be used to
-      reformat the text, so that the run parameter is added to the data as an additional column instead of a table
-      divider. Please Check LTSpiceExport class for more information.
-
-    + mout files - Files generated by the Plot File -> Execute .MEAS Script menu. This command allows the user to run
-      predefined .MEAS commands which create a .mout file. A .mout file has the measurement information stored in the
-      following format:
-
-      .. code-block:: text
-
-            Measurement: Vout_rms
-            step	RMS(V(OUT))	FROM	TO
-             1	1.41109	0	0.001
-             2	1.40729	0	0.001
-
-            Measurement: Vin_rms
-              step	RMS(V(IN))	FROM	TO
-                 1	0.706221	0	0.001
-                 2	0.704738	0	0.001
-
-            Measurement: gain
-              step	Vout_rms/Vin_rms
-                 1	1.99809
-                 2	1.99689
-
-
-The LTSteps.py can be used directly from a command line by invoking python with the -m option as exemplified below.
-
-.. code-block:: text
-
-    $ python -m PyLTSpice.LTSteps <path_to_filename>
-
-If `<path_to_filename>` is a log file, it will create a file with the same name, but with extension .tout that is a
-tab separated value (tsv) file, which contains the .STEP and .MEAS information collected.
-
-If `<path_to_filename>` is a txt exported file, it will create a file with the same name, but with extension .tsv a
-tab separated value (tsv) file, which contains data reformatted with the step number as one of the columns. Please
-consult the reformat_LTSpice_export() function for more information.
-
-If `<path_to_filename>` is a mout file, it will create a file with the same name, but with extension .tmout that is a
-tab separated value (tsv) file, which contains the .MEAS information collected, but adding the STEP run information
-as one of the columns.
-
-If `<path_to_filename>` argument is ommited, the script will automatically search for the newest .log/.txt/.mout file
-and use it.
-
-"""
-__author__ = "Nuno Canto Brum <me@nunobrum.com>"
-__copyright__ = "Copyright 2017, Fribourg Switzerland"
-
-import math
-import re
-import os
-import sys
-from collections import OrderedDict
-from typing import Union, Iterable, List
-from .detect_encoding import detect_encoding
-
-if __name__ == "__main__":
-    def message(*strs):
-        for string in strs:
-            print(string)
-else:
-
[docs] def message(*strs): - pass
- - -
[docs]class LTComplex(object): - """ - Class to represent complex numbers as exported by LTSpice - """ - complex_match = re.compile(r"\((?P<mag>[^dB]*)(dB)?,(?P<ph>.*)°\)") - - def __init__(self, strvalue): - a = self.complex_match.match(strvalue) - if a: - self.mag = float(a.group('mag')) - self.ph = float(a.group('ph')) - else: - raise ValueError("Invalid complex value format") - -
[docs] def to_complex(self): - ph = self.ph / 180 * math.pi - return complex(self.mag * math.cos(ph), self.mag * math.sin(ph))
- - def __str__(self): - return f"{self.mag},{self.ph}"
- - -
[docs]def try_convert_value(value: Union[str, int, float]) -> Union[int, float, str]: - """ - Tries to convert the string into an integer and if it fails, tries to convert to a float, if it fails, then returns the - value as string. - - :param value: value to convert - :type value: str, int or float - :return: converted value, if applicable - :rtype: int, float, str - """ - if isinstance(value, (int, float)): - return value - try: - ans = int(value) - except ValueError: - try: - ans = float(value) - except ValueError: - try: - ans = LTComplex(value) - except ValueError: - ans = value - return ans
- - -
[docs]def try_convert_values(values: Iterable[str]) -> List[Union[int, float, str]]: - """ - Same as try_convert_values but applicable to an iterable - - :param values: Iterable that returns strings - :type values: - :return: list with the values converted to either integer (int) or floating point (float) - :rtype: List[str] - """ - answer = [] - for value in values: - answer.append(try_convert_value(value)) - return answer
- - -
[docs]def reformat_LTSpice_export(export_file: str, tabular_file: str): - """ - Reads an LTSpice File Export file and writes it back in a format that is more convenient for data treatment. - - When using the "Export data as text" in the raw file menu the data is already exported in a tabular format. - However, if steps are being used, the step information doesn't appear on the table. Instead the successive STEP - runs are stacked on one after another, separated by the following text: - - .. code-block:: text - - Step Information: Ton=400m (Run: 2/2) - - What would be desirable would be that the step number (Run number) and the STEP variable would be placed within the - columns. This allows, for example, using Excel functionality known as Pivot Tables to filter out data, or some other - database selection function. - The tab is chosen as separator because it is normally compatible with pasting data into Excel. - - :param export_file: Filename of the .txt file generated by the "Export Data as Text" - :type export_file: str - :param tabular_file: Filename of the tab separated values (TSV) file that - :type tabular_file: str - :return: Nothing - :rtype: None - - """ - encoding = detect_encoding(export_file) - fin = open(export_file, 'r', encoding=encoding) - fout = open(tabular_file, 'w', encoding=encoding) - - headers = fin.readline() - # writing header - go_header = True - run_no = 0 # Just to avoid warning, this is later overridden by the step information - param_values = "" # Just to avoid warning, this is later overridden by the step information - regx = re.compile(r"Step Information: ([\w=\d\. -]+) +\(Run: (\d*)/\d*\)\n") - for line in fin: - if line.startswith("Step Information:"): - match = regx.match(line) - # message(line, end="") - if match: - # message(match.groups()) - step, run_no = match.groups() - # message(step, line, end="") - params = [] - for param in step.split(): - params.append(param.split('=')[1]) - param_values = "\t".join(params) - - if go_header: - header_keys = [] - for param in step.split(): - header_keys.append(param.split('=')[0]) - param_header = "\t".join(header_keys) - fout.write("Run\t%s\t%s" % (param_header, headers)) - message("Run\t%s\t%s" % (param_header, headers)) - go_header = False - # message("%s\t%s"% (run_no, param_values)) - else: - fout.write("%s\t%s\t%s" % (run_no, param_values, line)) - - fin.close() - fout.close()
- - -
[docs]class LTSpiceExport(object): - """ - Opens and reads LTSpice export data when using the "Export data as text" in the File Menu on the waveform window. - - The data is then accessible by using the following attributes implemented in this class. - - :property headers: list containing the headers on the exported data - :property dataset: dictionary in which the keys are the the headers and the export file and the values are - lists. When reading STEPed data, a new key called 'runno' is added to the dataset. - - **Examples** - - :: - - export_data = LTSpiceExport("export_data_file.txt") - for value in export_data.dataset['I(V1)']: - print(f"Do something with this value {value}") - - :param export_filename: path to the Export file. - :type export_filename: str - """ - - def __init__(self, export_filename: str): - self.encoding = detect_encoding(export_filename) - fin = open(export_filename, 'r', encoding=self.encoding) - file_header = fin.readline() - - self.headers = file_header.split('\t') - # Set to read header - go_header = True - - curr_dic = {} - self.dataset = {} - - regx = re.compile(r"Step Information: ([\w=\d\. -]+) +\(Run: (\d*)/\d*\)\n") - for line in fin: - if line.startswith("Step Information:"): - match = regx.match(line) - # message(line, end="") - if match: - # message(match.groups()) - step, run_no = match.groups() - # message(step, line, end="") - curr_dic['runno'] = run_no - for param in step.split(): - key, value = param.split('=') - curr_dic[key] = try_convert_value(value) - - if go_header: - go_header = False # This is executed only once - for key in self.headers: - self.dataset[key] = [] # Initializes an empty list - - for key in curr_dic: - self.dataset[key] = [] # Initializes an empty list - - else: - values = line.split('\t') - - for key in curr_dic: - self.dataset[key].append(curr_dic[key]) - - for i in range(len(values)): - self.dataset[self.headers[i]].append(try_convert_value(values[i])) - - fin.close()
- - -
[docs]class LTSpiceLogReader(object): - """ - Reads an LTSpice log file and retrieves the step information if it exists. The step information is then accessible - by using the 'stepset' property of this class. - This class is intended to be used together with the LTSpice_RawRead to retrieve the runs that are associated with a - given parameter setting. - - This class constructor only reads the step information of the log file. If the measures are needed, then the user - should call the get_measures() method. - - :property stepset: dictionary in which the keys are the variables that were STEP'ed during the simulation and - the associated value is a list representing the sequence of assigned values during simulation. - - :property headers: list containing the headers on the exported data. This is only populated when the *read_measures* - optional parameter is set to False. - - :property dataset: dictionary in which the keys are the the headers and the export file and the values are - lists. This is information is only populated when the *read_measures* optional parameter is set to False. - - :param log_filename: path to the Export file. - :type log_filename: str - :param read_measures: Optional parameter to skip measuring data reading. - :type read_measures: boolean - :param step_set: Optional parameter to provide the steps from another file. This is used to process .mout files. - :type step_set: dict - """ - - def __init__(self, log_filename: str, read_measures=True, step_set={}, encoding=None): - self.logname = log_filename - if encoding is None: - self.encoding = detect_encoding(log_filename, "Circuit:") - else: - self.encoding = encoding - self.step_count = len(step_set) - self.stepset = step_set.copy() # A copy is done since the dictionary is a mutable object. - # Changes in step_set would be propagated to object on the call - self.dataset = OrderedDict() # Dictionary in which the order of the keys is kept - self.measure_count = 0 - - # Preparing a stepless measurement read regular expression - # there are only measures taken in the format parameter: measurement - # A few examples of readings - # vout_rms: RMS(v(out))=1.41109 FROM 0 TO 0.001 => Interval - # vin_rms: RMS(v(in))=0.70622 FROM 0 TO 0.001 => Interval - # gain: vout_rms/vin_rms=1.99809 => Parameter - # vout1m: v(out)=-0.0186257 at 0.001 => Point - # fcutac=8.18166e+006 FROM 1.81834e+006 TO 1e+007 => AC Find Computation - regx = re.compile( - r"^(?P<name>\w+)(:\s+.*)?=(?P<value>[\d\.E+\-\(\)dB,°]+)(( FROM (?P<from>[\d\.E+-]*) TO (?P<to>[\d\.E+-]*))|( at (?P<at>[\d\.E+-]*)))?", - re.IGNORECASE) - - message("Processing LOG file", log_filename) - with open(log_filename, 'r', encoding=self.encoding) as fin: - line = fin.readline() - - while line: - if line.startswith("N-Period"): - import pandas as pd - # Read number of periods - n_periods = int(line.strip('\r\n').split("=")[-1]) - # Read waveform name - line = fin.readline().strip('\r\n') - waveform = line.split(" of ")[-1] - # Read DC component - line = fin.readline().strip('\r\n') - dc_component = float(line.split(':')[-1]) - # Skip blank line - fin.readline() - # Skip two header lines - fin.readline() - fin.readline() - - harmonic_lines = [] - while True: - line = fin.readline().strip('\r\n') - if line.startswith("Total Harmonic"): - # Find THD - thd = float(re.search(r"\d+.\d+", line).group()) - break - else: - harmonic_lines.append(line.replace("°","")) - - # Create Table - columns = [ - 'Harmonic Number', - 'Frequency [Hz]', - 'Fourier Component', - 'Normalized Component', - 'Phase [degree]', - 'Normalized Phase [degree]' - ] - harmonics_df = pd.DataFrame([r.split('\t') for r in harmonic_lines], columns=columns) - # Convert to numeric - harmonics_df = harmonics_df.apply(pd.to_numeric, errors='ignore') - - # Find Fundamental Frequency - frequency = harmonics_df['Frequency [Hz]'][0] - - # Save data related to this fourier analysis in a dictionary - data_dict = { - 'dc': dc_component, - 'thd': thd, - 'harmonics': harmonics_df - } - - # Find the dictionary that stores fourier data or create it if it does not exist - fourier_dict: dict = self.dataset.get('fourier', None) - if fourier_dict is None: - self.dataset['fourier'] = {} - fourier_dict = self.dataset['fourier'] - - # Find the dict that stores data for this frequency or create it if it does not exist - frequency_dict: dict = fourier_dict.get(frequency, None) - if frequency_dict is None: - fourier_dict[frequency] = {} - frequency_dict = fourier_dict[frequency] - - # Find the dict that stores data for this number of periods or create it if it does not exist - period_dict: dict = frequency_dict.get(n_periods, None) - if period_dict is None: - frequency_dict[n_periods] = {} - period_dict = frequency_dict[n_periods] - - # Find the list that stores data for this waveform or create it if it does not exist - waveform_list: list = period_dict.get(waveform, None) - if waveform_list is None: - period_dict[waveform] = [] - waveform_list = period_dict[waveform] - - # Add the data to the list - waveform_list.append(data_dict) - - if line.startswith(".step"): - # message(line) - self.step_count += 1 - tokens = line.strip('\r\n').split(' ') - for tok in tokens[1:]: - lhs, rhs = tok.split("=") - # Try to convert to int or float - rhs = try_convert_value(rhs) - - ll = self.stepset.get(lhs, None) - if ll: - ll.append(rhs) - else: - self.stepset[lhs] = [rhs] - - elif line.startswith("Measurement:"): - if not read_measures: - fin.close() - return - else: - break # Jumps to the section that reads measurements - - if self.step_count == 0: # then there are no steps, - match = regx.match(line) - if match: - # Get the data - dataname = match.group('name') - if match.group('from'): - headers = [dataname, dataname + "_FROM", dataname + "_TO"] - measurements = [match.group('value'), match.group('from'), match.group('to')] - elif match.group('at'): - headers = [dataname, dataname + "_at"] - measurements = [match.group('value'), match.group('at')] - else: - headers = [dataname] - measurements = [match.group('value')] - - for k, title in enumerate(headers): - self.dataset[title] = [ - try_convert_value(measurements[k])] # need to be a list for compatibility - line = fin.readline() - - # message("Reading Measurements") - dataname = None - - headers = [] # Initializing an empty parameters - measurements = [] - while line: - line = line.strip('\r\n') - if line.startswith("Measurement: "): - if dataname: # If previous measurement was saved - # store the info - if len(measurements): - message("Storing Measurement %s (count %d)" % (dataname, len(measurements))) - for k, title in enumerate(headers): - self.dataset[title] = [line[k] for line in measurements] - headers = [] - measurements = [] - dataname = line[13:] # text which is after "Measurement: ". len("Measurement: ") -> 13 - message("Reading Measurement %s" % line[13:]) - else: - tokens = line.split("\t") - if len(tokens) >= 2: - try: - int(tokens[0]) # This instruction only serves to trigger the exception - meas = tokens[1:] # [float(x) for x in tokens[1:]] - measurements.append(try_convert_values(meas)) - self.measure_count += 1 - except ValueError: - if len(tokens) >= 3 and (tokens[2] == "FROM" or tokens[2] == 'at'): - tokens[2] = dataname + '_' + tokens[2] - if len(tokens) >= 4 and tokens[3] == "TO": - tokens[3] = dataname + "_TO" - headers = [dataname] + tokens[2:] - measurements = [] - else: - message("->", line) - - line = fin.readline() # advance to the next line - - # storing the last data into the dataset - message("Storing Measurement %s" % dataname) - if len(measurements): - for k, title in enumerate(headers): - self.dataset[title] = [line[k] for line in measurements] - - message("%d measurements" % len(self.dataset)) - message("Identified %d steps, read %d measurements" % (self.step_count, self.measure_count)) - - def __getitem__(self, key): - """ - __getitem__ implements - :key: step or measurement name - :return: step or measurement set - :rtype: List[float] - """ - if isinstance(key, slice): - raise NotImplementedError("Slicing in not allowed in this class") - if key in self.stepset: - return self.stepset[key] - if key in self.dataset: - return self.dataset[key] # This will raise an Index Error if not found here. - raise IndexError("'%s' is not a valid step variable or measurement name" % key) - -
[docs] def steps_with_parameter_equal_to(self, param: str, value: Union[str, int, float]) -> List[int]: - """ - Returns the steps that contain a given condition. - - :param param: parameter identifier on LTSpice simulation - :type param: str - :param value: - :type value: - :return: List of positions that respect the condition of equality with parameter value - :rtype: List[int] - """ - condition_set = self.stepset[param] - # tries to convert the value to integer or float, for consistency with data loading implemetation - v = try_convert_value(value) - # returns the positions where there is match - return [i for i, a in enumerate(condition_set) if a == v]
- -
[docs] def steps_with_conditions(self, **conditions) -> List[int]: - """ - Returns the steps that respect one more more equality conditions - - :key conditions: parameters within the LTSpice simulation. Values are the matches to be found. - :return: List of steps that repect all the given conditions - :rtype: List[int] - """ - current_set = None - for param, value in conditions.items(): - condition_set = self.steps_with_parameter_equal_to(param, value) - if current_set is None: - # initialises the list - current_set = condition_set - else: - # makes the intersection between the lists - current_set = [v for v in current_set if v in condition_set] - return current_set
- -
[docs] def get_step_vars(self) -> List[str]: - """ - Returns the stepped variable names of . - :return: List of step variables. - :rtype: list of str - """ - return self.stepset.keys()
- -
[docs] def get_measure_names(self) -> List[str]: - """ - Returns the names of the measurements read from the log file. - :return: List of measurement names. - :rtype: list of str - """ - return self.dataset.keys()
- -
[docs] def get_measure_value(self, measure: str, step: int = None) -> Union[float, int, str, complex]: - """ - Returns a measure value on a given step. - - :param measure: name of the measurement to get - :type measure: str - :param step: optional step number if the simulation has no steps. - :type step: int - """ - if step is None: - if len(self.dataset[measure]) == 1: - return self.dataset[measure][0] - else: - raise IndexError("In stepped data, the step number needs to be provided") - else: - return self.dataset[measure][step]
- -
[docs] def get_measure_values_at_steps(self, measure: str, steps: Union[int, Iterable]): - """ - Returns the measurements taken at a list of steps provided by the steps list. - - :param measure: name of the measurement to get. - :type measure: str - :param steps: step number, or list of step numbers. - :type steps: int or list - :return: measurement or list of measurements - :rtype: int or Iterable - """ - if steps is None: - return self.dataset[measure] # Returns everything - elif isinstance(steps, int): - return self.dataset[measure][steps] - else: # Assuming it is an iterable - return [self.dataset[measure][step] for step in steps]
- -
[docs] def split_complex_values_on_datasets(self): - """ - Internal function to split the complex values into additional two columns - TODO: Delete the old data and insert new ones the the right position - """ - for param in list(self.dataset.keys()): - if len(self.dataset[param]) > 0 and isinstance(self.dataset[param][0], LTComplex): - self.dataset[param + '_mag'] = [v.mag for v in self.dataset[param]] - self.dataset[param + '_ph'] = [v.ph for v in self.dataset[param]]
- -
[docs] def export_data(self, export_file: str, append_with_line_prefix=None): - """ - Exports the measurement information to a tab separated value (.tsv) format. If step data is found, it is - included in the exported file. - - When using export data together with sim_batch.py classes, it may be helpful to append data to an existing - file. For this purpose, the user can user the append_with_line_prefix argument to indicate that an append should - be done. And in this case, the user must provide a string that will identify the LTSpice batch run. - - :param export_file: path to the file containing the information - :type export_file: str - :param append_with_line_prefix: user information to be written in the file in case an append is to be made. - :type append_with_line_prefix: str - :return: Nothing - """ - # message(tokens) - if append_with_line_prefix is None: - mode = 'w' # rewrites the file - else: - mode = 'a' # Appends an existing file - - if len(self.dataset) == 0: - print("Empty data set. Exiting without writing file.") - return - - fout = open(export_file, mode, encoding=self.encoding) - - if append_with_line_prefix is not None: # if an append it will write the filename first - fout.write('user info\t') - - fout.write("step\t%s\t%s\n" % ("\t".join(self.stepset.keys()), "\t".join(self.dataset))) - first_parameter = next(iter(self.dataset)) - for index in range(len(self.dataset[first_parameter])): - if self.step_count == 0: - step_data = [] # Empty step - else: - step_data = [self.stepset[param][index] for param in self.stepset.keys()] - meas_data = [self.dataset[param][index] for param in self.dataset] - - if append_with_line_prefix is not None: # if an append it will write the filename first - fout.write(append_with_line_prefix + '\t') - fout.write("%d" % (index + 1)) - for s in step_data: - fout.write(f'\t{s}') - - for tok in meas_data: - if isinstance(tok, list): - for x in tok: - fout.write(f'\t{x}') - else: - fout.write(f'\t{tok}') - fout.write('\n') - - fout.close()
- - -if __name__ == "__main__": - - def valid_extension(filename): - return filename.endswith('.txt') or filename.endswith('.log') or filename.endswith('.mout') - - - if len(sys.argv) > 1: - filename = sys.argv[1] - if not valid_extension(filename): - print("Invalid extension in filename '%s'" % filename) - print("This tool only supports the following extensions :'.txt','.log','.mout'") - exit(-1) - else: - filename = None - newer_date = 0 - for f in os.listdir(): - date = os.path.getmtime(f) - if date > newer_date and valid_extension(f): - newer_date = date - filename = f - if filename is None: - print("File not found") - print("This tool only supports the following extensions :'.txt','.log','.mout'") - exit(-1) - - fname_out = None - if filename.endswith('.txt'): - fname_out = filename[:-len('txt')] + 'tsv' - elif filename.endswith('.log'): - fname_out = filename[:-len('log')] + 'tlog' - elif filename.endswith('.mout'): - fname_out = filename[:-len('mout')] + 'tmout' - else: - print("Error in file type") - print("This tool only supports the following extensions :'.txt','.log','.mout'") - exit(-1) - - if fname_out is not None: - print("Processing File %s" % filename) - print("Creating File %s" % fname_out) - if filename.endswith('txt'): - print("Processing Data File") - reformat_LTSpice_export(filename, fname_out) - elif filename.endswith("log"): - data = LTSpiceLogReader(filename) - data.split_complex_values_on_datasets() - data.export_data(fname_out) - elif filename.endswith(".mout"): - log_file = filename[:len('mout')] + 'log' - if os.path.exists(log_file): - steps = LTSpiceLogReader(log_file, read_measures=False) - data = LTSpiceLogReader(filename, step_set=steps.stepset) - data.stepset = steps.stepset - else: - # just reformats - data = LTSpiceLogReader(filename) - data.split_complex_values_on_datasets() - data.export_data(fname_out) - data.export_data(fname_out) - - # input("Press Enter to Continue") -
- -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/_modules/PyLTSpice/SemiDevOpReader.html b/doc_build/html/_modules/PyLTSpice/SemiDevOpReader.html deleted file mode 100644 index 79dd529..0000000 --- a/doc_build/html/_modules/PyLTSpice/SemiDevOpReader.html +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - PyLTSpice.SemiDevOpReader — PyLTSpice 3.0 documentation - - - - - - - - - - - - -
-
- -
- -
-
-
- -

Source code for PyLTSpice.SemiDevOpReader

-#!/usr/bin/env python
-# coding=utf-8
-
-# -------------------------------------------------------------------------------
-#    ____        _   _____ ____        _
-#   |  _ \ _   _| | |_   _/ ___| _ __ (_) ___ ___
-#   | |_) | | | | |   | | \___ \| '_ \| |/ __/ _ \
-#   |  __/| |_| | |___| |  ___) | |_) | | (_|  __/
-#   |_|    \__, |_____|_| |____/| .__/|_|\___\___|
-#          |___/                |_|
-#
-# Name:        SemiDevOpReader.py
-# Purpose:     Read Semiconductor Device Operating Points from a log file
-#
-# Author:      Nuno Brum (nuno.brum@gmail.com)
-#
-# Created:     19-09-2021
-# Licence:     refer to the LICENSE file
-# -------------------------------------------------------------------------------
-
-"""
-Implements a parser for extracting Semiconductor Devices Operating Points from an LTSpice log file.
-"""
-
-import re
-from .detect_encoding import detect_encoding
-
-
-
[docs]def opLogReader(filename: str) -> dict: - """ - This function is exclusively dedicated to retrieving operation point parameters of Semiconductor Devices. This is - handled separately from the main LogReader class because of its specialization and therefore not judged to be - of interest to the typical LTSpice user making board level simulations. - - Below is an excerpt of a Semiconductor Device Operating Points log.py - - .. code-block:: text - - Semiconductor Device Operating Points: - - --- Diodes --- - Name: d:m6:1:para2:1 d:m4:1:para2:1 d:m3:1:para2:1 d:m6:1:para1:2 - Model: m6:1:para2:dpar_5v_psubnburd m4:1:para2:dpar_5v_psubnburd m3:1:para2:dpar_5v_psubnburd dpar_pburdnburdsw - Id: 3.45e-19 3.45e-19 3.45e-19 1.11e-13 - Vd: 3.27e-07 3.27e-07 3.27e-07 9.27e-02 - ... - CAP: 3.15e-14 3.15e-14 3.15e-14 5.20e-14 - - --- Bipolar Transistors --- - Name: q:q2:1:2 q:q2:1:1 q:q1:1:2 q:q1:1:1 q:q7:3 - Model: q2:1:qnl_pnp q2:1:qnl_m q1:1:qnl_pnp q1:1:qnl_m qpl_parc - Ib: 3.94e-12 4.69e-08 7.43e-13 4.70e-08 3.75e-12 - Ic: -2.34e-12 4.57e-06 -7.44e-13 4.50e-06 -2.35e-12 - Vbe: 1.60e+00 7.40e-01 -7.88e-04 7.40e-01 1.40e+00 - - - This function will parse the log file and will produce a dictionary that contains all the information retrieved with - the following format: - - .. code-block:: python - - semi_ops = { - 'Diodes': { - 'd:m6:1:para2:1': { - 'Model': 'm6:1:para2:dpar_5v_psubnburd', 'Id': 3.45e-19, 'Vd': 3.27e-07, ... 'CAP': 3.15e-14 }, - 'd:m4:1:para2:1': { - 'Model': 'm4:1:para2:dpar_5v_psubnburd', 'Id': 3.45e-19, 'Vd': 3.27e-07, ..., 'CAP': 3.15e-14 }, - 'd:m3:1:para2:1': { - 'Model': 'm3:1:para2:dpar_5v_psubnburd', 'Id': 3.45e-19, 'Vd': 3.27e-07, ..., 'CAP': 3.15e-14 }, - 'd:m6:1:para1:2': { - 'Model': 'dpar_pburdnburdsw', 'Id': 1.11e-13, 'Vd': 0.0927, ..., 'CAP': 5.2e-14 }, - }, - 'Bipolar Transistors': { - 'q:q2:1:2': { - 'Model': 'q2:1:qnl_pnp', 'Ib': 3.94e-12, 'Ic': -2.34e-12, 'Vbe': 160, ... }, - 'q:q2:1:1': { - 'Model': 'q2:1:qnl_m', 'Ib': 4.69e-08, 'Ic': 4.57e-06, 'Vbe': 0.074, ... }, - 'q:q1:1:2': { - 'Model': 'q1:1:qnl_pnp', 'Ib': 7.43e-13, 'Ic': -7.44e-13, 'Vbe': -0.000788, ... }, - 'q:q1:1:1': { - 'Model': 'q1:1:qnl_m', 'Ib': 4.7e-08, 'Ic': 4.5e-06, 'Vbe': 0.74, ... }, - 'q:q7:3': { - 'Model': 'qpl_parc', 'Ib': 3.75e-12, 'Ic': -2.35e-12, 'Vbe': 1.4, ... }, - }, - } - - - :param filename: path to the log file containing the Semiconductor Device Operating Points - :type filename: str - :return: Dictionary containing the information as described above. - :rtype: dict - """ - dataset = {} - is_title = re.compile(r"^\s*--- (.*) ---\s*$") - encoding = detect_encoding(filename) - log = open(filename, 'r', encoding=encoding) - where = None - n_devices = 0 - line = None - for line in log: - if line.startswith("Semiconductor Device Operating Points:"): - break - - if line is not None and line.startswith("Semiconductor Device Operating Points:"): - for line in log: - match = is_title.search(line) - if match is not None: - where = match.group(1) - dataset[where] = {} # Creates a dictionary for each component type - else: - cols = re.split(r'\s+', line.rstrip('\r\n')) - if len(cols) > 1 and (cols[0].endswith(":") or cols[0] == 'Gmb'): # The last 'or condition solves an - # LTSpice bug where the Gmb parameter is not suffixed by : - Thanks to Amitkumar for finding this. - if cols[0] == "Name:": - devices = cols[1:] - n_devices = len(devices) - for dev in cols[1:]: - dataset[where][dev] = {} - else: - if n_devices > 0 and len(cols) == (n_devices + 1): - param = cols[0].rstrip(':') - for i, val in enumerate(cols[1:]): - try: - value = float(val) - except ValueError: - value = val - dataset[where][devices[i]][param] = value - log.close() - return dataset
-
- -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/_modules/PyLTSpice/SpiceEditor.html b/doc_build/html/_modules/PyLTSpice/SpiceEditor.html deleted file mode 100644 index 8d8d692..0000000 --- a/doc_build/html/_modules/PyLTSpice/SpiceEditor.html +++ /dev/null @@ -1,1123 +0,0 @@ - - - - - - - - PyLTSpice.SpiceEditor — PyLTSpice 3.0 documentation - - - - - - - - - - - - -
-
- -
- -
-
-
- -

Source code for PyLTSpice.SpiceEditor

-#!/usr/bin/env python
-# coding=utf-8
-
-# -------------------------------------------------------------------------------
-#    ____        _   _____ ____        _
-#   |  _ \ _   _| | |_   _/ ___| _ __ (_) ___ ___
-#   | |_) | | | | |   | | \___ \| '_ \| |/ __/ _ \
-#   |  __/| |_| | |___| |  ___) | |_) | | (_|  __/
-#   |_|    \__, |_____|_| |____/| .__/|_|\___\___|
-#          |___/                |_|
-#
-# Name:        SpiceEditor.py
-# Purpose:     Class made to update Generic Spice Netlists
-#
-# Author:      Nuno Brum (nuno.brum@gmail.com)
-#
-# Created:     30-08-2020
-# Licence:     refer to the LICENSE file
-# -------------------------------------------------------------------------------
-import os
-import traceback
-import re
-import logging
-from math import log, floor
-from typing import Union, Optional, List
-from .detect_encoding import detect_encoding
-
-__author__ = "Nuno Canto Brum <nuno.brum@gmail.com>"
-__copyright__ = "Copyright 2021, Fribourg Switzerland"
-
-END_LINE_TERM = '\n'  #: This controls the end of line terminator used
-SUBCIRCUIT_DIVIDER = ':'  #: This controls the Subcircuit divider when setting component values inside subcircuits.
-                          # Ex: Editor.set_component
-
-# A Spice netlist can only have one of the instructions below, otherwise an error will be raised
-UNIQUE_SIMULATION_DOT_INSTRUCTIONS = ('.AC', '.DC', '.TRAN', '.NOISE', '.DC', '.TF')
-
-SPICE_DOT_INSTRUCTIONS = (
-    '.BACKANNO',
-    '.END',
-    '.ENDS',
-    '.FERRET', # Downloads a File from a given URL
-    '.FOUR',  # Compute a Fourier Component after a .TRAN Analysis
-    '.FUNC', '.FUNCTION',
-    '.GLOBAL',
-    '.IC',
-    '.INC', '.INCLUDE',  # Include another file
-    '.LIB', # Include a Library
-    '.LOADBIAS', # Load a Previously Solved DC Solution
-     # These Commands are part of the contraption Programming Language of the Arbitrary State Machine
-    '.MACHINE', '.STATE', '.RULE', '.OUTPUT', '.ENDMACHINE',
-    '.MEAS', '.MEASURE',
-    '.MODEL',
-    '.NET', # Compute Network Parameters in a .AC Analysis
-    '.NODESET',  # Hints for Initial DC Solution
-    '.OP',
-    '.OPTIONS',
-    '.PARAM', '.PARAMS',
-    '.SAVE', '.SAV',
-    '.SAVEBIAS',
-    '.STEP',
-    '.SUBCKT',
-    '.TEXT',
-    '.WAVE', # Write Selected Nodes to a .Wav File
-
-)
-
-REPLACE_REGXES = {
-    'A': r"",  # Special Functions, Parameter substitution not supported
-    'B': r"^(?P<designator>B§?[VI]?\w+)(?P<nodes>(\s+\S+){2})\s+(?P<value>.*)$",  # Behavioral source
-    'C': r"^(?P<designator>C§?\w+)(?P<nodes>(\s+\S+){2})(?P<model>\s+\w+)?\s+(?P<value>({)?(?(6).*}|([0-9\.E+-]+(Meg|[kmuµnpf])?F?))).*$",  # Capacitor
-    'D': r"^(?P<designator>D§?\w+)(?P<nodes>(\s+\S+){2})\s+(?P<value>\w+).*$",  # Diode
-    'I': r"^(?P<designator>I§?\w+)(?P<nodes>(\s+\S+){2})\s+(?P<value>.*)$",  # Current Source
-    'E': r"^(?P<designator>E§?\w+)(?P<nodes>(\s+\S+){2,4})\s+(?P<value>.*)$",  # Voltage Dependent Voltage Source
-                                                        # this only supports changing gain values
-    'F': r"^(?P<designator>F§?\w+)(?P<nodes>(\s+\S+){2})\s+(?P<value>.*)$",  # Current Dependent Current Source
-                                                        # This implementation replaces everything after the 2 first nets
-    'G': r"^(?P<designator>G§?\w+)(?P<nodes>(\s+\S+){2,4})\s+(?P<value>.*)$",  # Voltage Dependent Current Source
-                                                        # This only supports changing gain values
-    'H': r"^(?P<designator>H§?\w+)(?P<nodes>(\s+\S+){2})\s+(?P<value>.*)$",  # Voltage Dependent Current Source
-                                                        # This implementation replaces everything after the 2 first nets
-    'I': r"^(?P<designator>I§?\w+)(?P<nodes>(\s+\S+){2})\s+(?P<value>.*)$",  # Current Source
-                                                        # This implementation replaces everything after the 2 first nets
-    'J': r"^(?P<designator>J§?\w+)(?P<nodes>(\s+\S+){3})\s+(?P<value>\w+).*$",  # JFET
-    'K': r"^(?P<designator>K§?\w+)(?P<nodes>(\s+\S+){2,4})\s+(?P<value>[\+\-]?[0-9\.E+-]+[kmuµnpf]?).*$",  # Mutual Inductance
-    'L': r"^(?P<designator>L§?\w+)(?P<nodes>(\s+\S+){2})\s+(?P<value>({)?(?(5).*}|([0-9\.E+-]+(Meg|[kmuµnpf])?H?))).*$",  # Inductance
-    'M': r"^(?P<designator>M§?\w+)(?P<nodes>(\s+\S+){3,4})\s+(?P<value>\w+).*$",  # MOSFET TODO: Parameters substitution not supported
-    'O': r"^(?P<designator>O§?\w+)(?P<nodes>(\s+\S+){4})\s+(?P<value>\w+).*$",  # Lossy Transmission Line TODO: Parameters substitution not supported
-    'Q': r"^(?P<designator>Q§?\w+)(?P<nodes>(\s+\S+){3,4})\s+(?P<value>\w+).*$",  # Bipolar TODO: Parameters substitution not supported
-    '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
-    'S': r"^(?P<designator>S§?\w+)(?P<nodes>(\s+\S+){4})\s+(?P<value>.*)$",  # Voltage Controlled Switch
-    'T': r"^(?P<designator>T§?\w+)(?P<nodes>(\s+\S+){4})\s+(?P<value>.*)$",  # Lossless Transmission
-    'U': r"^(?P<designator>U§?\w+)(?P<nodes>(\s+\S+){3})\s+(?P<value>.*)$",  # Uniform RC-line
-    'V': r"^(?P<designator>V§?\w+)(?P<nodes>(\s+\S+){2})\s+(?P<value>.*)$",  # Voltage Source
-                                                        # This implementation replaces everything after the 2 first nets
-    'W': r"^(?P<designator>W§?\w+)(?P<nodes>(\s+\S+){2})\s+(?P<value>.*)$",  # Current Controlled Switch
-                                                        # This implementation replaces everything after the 2 first nets
-    'X': r"^(?P<designator>X§?\w+)(?P<nodes>(\s+\S+){1,99}?)\s+(?P<value>\w+)(\s+params:)?(?P<params>(\s+\w+\s*=\s*[\d\w{}()\-\+\*/]+)*)\s*\\?$",  # Sub-circuit, Parameter substitution not supported
-    'Z': r"^(?P<designator>Z§?\w+)(?P<nodes>(\s+\S+){3})\s+(?P<value>\w+).*$",  # MESFET and IBGT. TODO: Parameters substitution not supported
-}
-
-
-PARAM_REGEX = r"(?<= )(?P<replace>%s(\s*=\s*)(?P<value>[\w\*\/\.\+\-\/\*\{\}\(\)\t ]*))(?<!\s)($|\s+)(?!\s*=)"
-SUBCKT_CLAUSE_FIND = r"^.SUBCKT\s+"
-
-# Code Optimization objects, avoiding repeated compilation of regular expressions
-component_replace_regexs = {prefix: re.compile(pattern, re.IGNORECASE) for prefix, pattern in REPLACE_REGXES.items()}
-subcircuit_regex = re.compile(r"^.SUBCKT\s+(?P<name>\w+)", re.IGNORECASE)
-lib_inc_regex = re.compile(r"^\.(LIB|INC)\s+(.*)$", re.IGNORECASE)
-
-LibSearchPaths = []
-
-def format_eng(value) -> str:
-    """
-    Helper function for formating value with the SI qualifiers.  That is, it will use
-
-        * p for pico (10E-12)
-        * n for nano (10E-9)
-        * u for micro (10E-6)
-        * m for mili (10E-3)
-        * k for kilo (10E+3)
-        * Meg for Mega (10E+6)
-
-
-    :param value: float value to format
-    :type value: float
-    :return: String wiht the formatted value
-    :rtype: str
-    """
-    if value == 0.0:
-        return "0.0"  # This avoids a problematic log(0)
-    e = floor(log(abs(value), 1000))
-    if -5 <= e < 0:
-        suffix = "fpnum"[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)
-
-
-def scan_eng(value: str) -> float:
-    """
-    Converts a string to a float, considering SI multipliers
-
-        * f for femto (10E-15)
-        * p for pico (10E-12)
-        * n for nano (10E-9)
-        * u or µ for micro (10E-6)
-        * m for mili (10E-3)
-        * k for kilo (10E+3)
-        * Meg for Mega (10E+6)
-
-    The extra unit qualifiers such as V for volts or F for Farads are ignored.
-
-
-    :param value: string to be converted to float
-    :type value: str
-    :return:
-    :rtype: float
-    :raises: ValueError when the value cannot be converted.
-    """
-    # Search for the last digit on the string. Assuming that all after the last number are SI qualifiers and units.
-    value = value.strip()
-    x = len (value)
-    while x > 0:
-        if value[x-1] in "0123456789":
-            break
-        x -= 1
-    suffix = value[x:]  # this is the non numeric part at the end
-    f = float(value[:x])  # this is the numeric part. Can raise ValueError.
-    if suffix:
-        if suffix[0] in "fpnuµmk":
-            return f * {
-                'f': 1.0e-15,
-                'p': 1.0e-12,
-                'n': 1.0e-09,
-                'u': 1.0e-06,
-                'µ': 1.0e-06,
-                'm': 1.0e-03,
-                'k': 1.0e+03,
-            }[suffix[0]]
-        elif suffix.startswith("Meg"):
-            return f * 1E+6
-    return f
-
-
-def get_line_command(line) -> str:
-    """
-    Retrives the type of SPICE command in the line.
-    Starts by removing the leading spaces and the evaluates if it is a comment, a directive or a component.
-    """
-    if isinstance(line, str):
-        for i in range(len(line)):
-            ch = line[i]
-            if ch == ' ' or ch == '\t':
-                continue
-            else:
-                ch = ch.upper()
-                if ch in REPLACE_REGXES:  # A circuit element
-                    return ch
-                elif ch == '+':
-                    return '+'  # This is a line continuation.
-                elif ch in "#;*\n\r":  # It is a comment or a blank line
-                    return "*"
-                elif ch == '.':  # this is a directive
-                    j = i + 1
-                    while j < len(line) and (line[j] not in (' ', '\t', '\r', '\n')):
-                        j += 1
-                    return line[i:j].upper()
-                else:
-                    raise SyntaxError('Unrecognized command in line "%s"' % line)
-    elif isinstance(line, SpiceCircuit):
-        return ".SUBCKT"
-    else:
-        raise SyntaxError('Unrecognized command in line "{}"'.format(line))
-
-
-def _first_token_upped(line):
-    """
-    (Private function. Not to be used directly)
-    Returns the first non space character in the line. If a point '.' is found, then it gets the primitive associated.
-    """
-    i = 0
-    while i < len(line) and line[i] in (' ', '\t'):
-        i += 1
-    j = i
-    while i < len(line) and not (line[i] in (' ', '\t')):
-        i += 1
-    return line[j:i].upper()
-
-def _is_unique_instruction(instruction):
-    """
-    (Private function. Not to be used directly)
-    Returns true if the instruction is one of the unique instructions
-    """
-    cmd = get_line_command(instruction)
-    return cmd in UNIQUE_SIMULATION_DOT_INSTRUCTIONS
-
-
-class ComponentNotFoundError(Exception):
-    """Component Not Found Error"""
-
-class ParameterNotFoundError(Exception):
-    """ParameterNotFound Error"""
-    def __init__(self, parameter):
-        super().__init__(f'Parameter "{parameter}" not found')
-
-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}"')
-
-
-
[docs]class SpiceCircuit(object): - """ - The Spice Circuit represents subcircuits within a SPICE circuit and since subcircuits can have subcircuits inside - them, it serves as base for the top level netlist. - This hierchical approach helps to encapsulate and protect parameters and components from a edits made a a higher - level. - The information is stored in a python list, each line of the SPICE netlist is an item of the list. A Subcircuit - is represented as a SpiceCircuit object. - """ - - def __init__(self): - self.subcircuits = {} - self.netlist = [] - self.logger = logging.getLogger("SpiceCircuit") - - def _getline_startingwith(self, substr: str) -> int: - """Internal function. Do not use.""" - # This function returns the line number that starts with the substr string. - # If the line is not found, then -1 is returned. - substr_upper = substr.upper() - for line_no, line in enumerate(self.netlist): - if isinstance(line, SpiceCircuit): # If it is a subcircuit it will simply ignore it. - continue - line_upcase = _first_token_upped(line) - if line_upcase == substr_upper: - return line_no - error_msg = "line starting with '%s' not found in netlist" % substr - self.logger.error(error_msg) - raise ComponentNotFoundError(error_msg) - - def _add_lines(self, line_iter): - """Internal function. Do not use. - Add a list of lines to the netlist.""" - for line in line_iter: - cmd = get_line_command(line) - if cmd == '.SUBCKT': - sub_circuit = SpiceCircuit() - sub_circuit.netlist.append(line) - # Advance to the next non nested .ENDS - finished = sub_circuit._add_lines(line_iter) - if finished: - self.netlist.append(sub_circuit) - else: - return False - elif cmd == '+': - assert len(self.netlist) > 0, "ERROR: The first line cannot be starting with a +" - self.netlist[-1] += line # Appends to the last line - else: - self.netlist.append(line) - if cmd[:4] == '.END': # True for either .END and .ENDS primitives - return True # If an subcircuit is ended correctly, returns True - return False # If a subcircuit ends abruptly, returns False - - def _write_lines(self, f): - """Internal function. Do not use.""" - # This helper function writes the contents of Subcircuit to the file f - for command in self.netlist: - if isinstance(command, SpiceCircuit): - command._write_lines(f) - else: - f.write(command) - - def _get_line_matching(self, command, search_expression): - """ - Internal function. Do not use. Returns a line starting with command and matching the search with the regular - expression - """ - in_param_line = False # This is needed to process multi-line commands - line_no = 0 - while line_no < len(self.netlist): - line = self.netlist[line_no] - if isinstance(line, SpiceCircuit): # If it is a subcircuit it will simply ignore it. - line_no += 1 - continue - cmd = get_line_command(line) - if cmd == command: - match = search_expression.search(line) - if match: - return line_no, match - line_no += 1 - return -1, None # If it fails, it returns an invalid line number and No match - - def _get_subcircuit(self, instance_name: str) -> 'SubCircuit': - """Internal function. Do not use.""" - global LibSearchPaths - if SUBCIRCUIT_DIVIDER in instance_name: - subcircuit_ref, sub_subcircuits = instance_name.split(SUBCIRCUIT_DIVIDER, 1) - else: - subcircuit_ref = instance_name - - line_no = self._getline_startingwith(subcircuit_ref) - sub_circuit_instance = self.netlist[line_no] - regex = component_replace_regexs['X'] # The subcircuit instance regex - m = regex.search(sub_circuit_instance) - if m: - subcircuit_name = m.group('value') # last_token of the line before Params: - else: - raise UnrecognizedSyntaxError(sub_circuit_instance, REPLACE_REGXES['X']) - - line_no = 0 - reg_subckt = re.compile(SUBCKT_CLAUSE_FIND + subcircuit_name, re.IGNORECASE) - - libs_list = [] - sub_circuit = None - while line_no < len(self.netlist): - line = self.netlist[line_no] - if isinstance(line, SpiceCircuit) and line.name() == subcircuit_name: - sub_circuit = line # The circuit was already found - break - else: - m = lib_inc_regex.match(line) - if m: # For compatibility issues not using the walruss operator here - libs_list.append( m.group(2) ) - line_no += 1 - if sub_circuit is None: - # If we reached here is because the subciruit was not found. Search for it in declared libraries - libs_list_full_path = [] - for lib in libs_list: - if os.path.exists(lib): - libs_list_full_path.append(lib) - continue - lib_filename = os.path.join(os.path.expanduser('~'),"Documents\\LTspiceXVII\\lib\\sub", lib) - if os.path.exists(lib_filename): - libs_list_full_path.append(lib) - continue - for path in LibSearchPaths: - lib_filename= os.path.join(path, lib) - if os.path.exists(lib_filename): - libs_list_full_path.append(lib_filename) - continue - - - # If it reached here, we have a valid lib_filename - for lib_path in libs_list_full_path: - sub_circuit = SpiceEditor.find_subckt_in_lib(lib_path, subcircuit_name) - if sub_circuit: - break - if sub_circuit: - if SUBCIRCUIT_DIVIDER in instance_name: - return sub_circuit._get_subcircuit(sub_subcircuits) - else: - return sub_circuit - else: - # The search was not successful - raise ComponentNotFoundError(f'Subcircuit "{subcircuit_name}" not found') - - - def _set_model_and_value(self, component, value): - """Internal function. Do not use.""" - prefix = component[0] # Using the first letter of the component to identify what is it - regex = component_replace_regexs.get(prefix, None) # Obtain RegX to make the update - - if regex is None: - print("Component must start with one of these letters:\n", ','.join(REPLACE_REGXES.keys())) - print("Got '{}'".format(component)) - return - - if isinstance(value, (int, float)): - value = format_eng(value) - - line_no = self._getline_startingwith(component) - - line = self.netlist[line_no] - m = regex.match(line) - if m is None: - raise UnrecognizedSyntaxError(line, REPLACE_REGXES[prefix]) - # print("Unsupported line ""{}""".format(line)) - else: - start = m.start('value') - end = m.end('value') - self.netlist[line_no] = line[:start] + value + line[end:] - -
[docs] def clone(self, **kwargs) -> 'SpiceCircuit': - """ - Creates a new copy of the SpiceCircuit. Change done at the new copy are not affecting the original - - :key new_name: The new name to be given to the circuit - :type new_name: str - :return: The new replica of the SpiceCircuit object - :rtype: SpiceCircuit - """ - clone = SpiceCircuit() - clone.netlist = self.netlist.copy() - clone.netlist.insert(0, "***** SpiceEditor Manipulated this subcircuit ****" + END_LINE_TERM) - clone.netlist.append("***** ENDS SpiceEditor ****" + END_LINE_TERM) - new_name = kwargs.get('new_name', None) - if new_name: # If it is different from None - clone.setname(new_name) - return clone
- -
[docs] def name(self): - if len(self.netlist): - for line in self.netlist: - m = subcircuit_regex.search(line) - if m: - return m.group('name') - else: - raise RuntimeError("Unable to find .SUBCKT clause in subcircuit") - else: - raise RuntimeError("Empty Subcircuit")
- -
[docs] def setname(self, new_name: str): - """ - Renames the subcircuit to a new name. No check is done to the new game give. It is up to the user to make sure - that the new name is valid. - - :param new_name: The new Name. - :type new_name: str - :return: Nothing - :rtype: None - """ - if len(self.netlist): - lines = len(self.netlist) - line_no = 0 - while line_no < lines: - line = self.netlist[line_no] - m = subcircuit_regex.search(line) - if m: - # Replacing the name in the SUBCKT Clause - start = m.start('name') - end = m.end('name') - self.netlist[line_no] = line[:start] + new_name + line[end:] - break - line_no += 1 - else: - raise UnrecognizedSyntaxError("Unable to find .SUBCKT clause in subcircuit") - - # This second loop finds the .ENDS clause - while line_no < lines: - line = self.netlist[line_no] - if get_line_command(line) == '.ENDS': - self.netlist[line_no] = '.ENDS ' + new_name + END_LINE_TERM - break - line_no += 1 - else: - raise UnrecognizedSyntaxError("Unable to find .SUBCKT clause in subcircuit") - else: - # Avoiding exception by creating an empty subcircuit - self.netlist.app("* SpiceEditor Created this subcircuit") - self.netlist.append('.SUBCKT %s%s' % (new_name, END_LINE_TERM)) - self.netlist.append('.ENDS %s%s' % (new_name, END_LINE_TERM))
- -
[docs] def get_component_info(self, component) -> dict: - """ - Retrieves the component information as defined in the corresponding REGEX. The line number is also added. - - :param component: Reference of the component - :type component: str - :return: Dictionary with the component information - :rtype: dict - :raises: UnrecognizedSyntaxError when the line doesn't match the expected REGEX. NotImplementedError of there - isn't an associated regular expression for the component prefix. - """ - prefix = component[0] # Using the first letter of the component to identify what is it - regex = component_replace_regexs.get(prefix, None) # Obtain RegX to make the update - - if regex is None: - self.logger.warning("Component must start with one of these letters:\n", ','.join(REPLACE_REGXES.keys())) - self.logger.warning("Got '{}'".format(component)) - raise NotImplementedError("Unsuported prefix {}".format(prefix)) - - line_no = self._getline_startingwith(component) - line = self.netlist[line_no] - m = regex.match(line) - if m is None: - error_msg = 'Unsupported line "{}"\nExpected format is "{}"'.format(line, REPLACE_REGXES[prefix]) - self.logger.error(error_msg) - raise UnrecognizedSyntaxError(error_msg) - - info = m.groupdict() - info['line'] = line_no # adding the line number to the component information - return info
- -
[docs] def get_parameter(self, param: str) -> str: - """ - Retrieves a Parameter from the Netlist - - :param param: Name of the parameter to be retrieved - :type param: str - :return: Value of the parameter being sought - :rtype: str - :raises: ParameterNotFoundError - In case the component is not found - """ - regx = re.compile(PARAM_REGEX % param, re.IGNORECASE) - line_no, match = self._get_line_matching('.PARAM', regx) - if match: - return match.group('value') - else: - raise ParameterNotFoundError(param)
- -
[docs] def set_parameter(self, param: str, value: Union[str, int, float]) -> None: - """Adds a parameter to the SPICE netlist. - - Usage: :: - - LTC.set_parameter("TEMP", 80) - - This adds onto the netlist the following line: :: - - .PARAM TEMP=80 - - This is an alternative to the set_parameters which is more pythonic in it's usage, - and allows setting more than one parameter at once. - - :param param: Spice Parameter name to be added or updated. - :type param: str - - :param value: Parameter Value to be set. - :type value: str, int or float - - :return: Nothing - """ - regx = re.compile(PARAM_REGEX % param, re.IGNORECASE) - param_line, match = self._get_line_matching('.PARAM', regx) - if match: - start, stop = match.span(regx.groupindex['replace']) - line = self.netlist[param_line] - self.netlist[param_line] = line[:start] + "{}={}".format(param, value) + line[stop:] - else: - # Was not found - # the last two lines are typically (.backano and .end) - insert_line = len(self.netlist) - 2 - self.netlist.insert(insert_line, '.PARAM {}={} ; Batch instruction'.format(param, value) + END_LINE_TERM)
- -
[docs] def set_parameters(self, **kwargs): - """Adds one or more parameters to the netlist. - Usage: :: - - for temp in (-40, 25, 125): - for freq in sweep_log(1, 100E3,): - LTC.set_parameters(TEMP=80, freq=freq) - - :key param_name: - Key is the parameter to be set. values the ther corresponding values. Values can either be a str; an int or - a float. - - :returns: Nothing - """ - for param in kwargs: - self.set_parameter(param, kwargs[param])
- -
[docs] def set_component_value(self, device: str, value: Union[str, int, float]) -> None: - """Changes the value of a component, such as a Resistor, Capacitor or Inductor. For components inside - subcircuits, use the subcirciut designator prefix with ':' as separator (Example X1:R1) - Usage: :: - - LTC.set_component_value('R1', '3.3k') - LTC.set_component_value('X1:C1', '10u') - - :param device: Reference of the circuit element to be updated. - :type device: str - :param value: - value to be be set on the given circuit element. Float and integer values will automatically - formatted as per the engineering notations 'k' for kilo, 'm', for mili and so on. - :type value: str, int or float - :raises: - ComponentNotFoundError - In case the component is not found - - ValueError - In case the value doesn't correspond to the expected format - - NotImplementedError - In case the circuit element is defined in a format which is not supported by this - version. - - If this is the case, use GitHub to start a ticket. https://github.com/nunobrum/PyLTSpice - """ - self._set_model_and_value(device, value)
- -
[docs] def set_element_model(self, element: str, model: str) -> None: - """Changes the value of a circuit element, such as a diode model or a voltage supply. - Usage: :: - - LTC.set_element_model('D1', '1N4148') - LTC.set_element_model('V1' "SINE(0 1 3k 0 0 0)") - - :param element: Reference of the circuit element to be updated. - :type element: str - :param model: model name of the device to be updated - :type model: str - - :raises: - ComponentNotFoundError - In case the component is not found - - ValueError - In case the model format contains irregular characters - - NotImplementedError - In case the circuit element is defined in a format which is not supported by this version. - - If this is the case, use GitHub to start a ticket. https://github.com/nunobrum/PyLTSpice - """ - self._set_model_and_value(element, model)
- -
[docs] def get_component_value(self, element: str) -> str: - """ - Returns the value of a component retrieved from the netlist. - - :param element: Reference of the circuit element to get the value. - :type element: str - - :return: value of the circuit element . - :rtype: str - - :raises: ComponentNotFoundError - In case the component is not found - - NotImplementedError - for not supported operations - """ - return self.get_component_info(element)['value']
- -
[docs] def get_component_floatvalue(self, element: str) -> str: - """ - Returns the value of a component retrieved from the netlist. - - :param element: Reference of the circuit element to get the value in float format. - :type element: str - - :return: value of the circuit element in float type - :rtype: float - - :raises: ComponentNotFoundError - In case the component is not found - - NotImplementedError - for not supported operations - """ - return scan_eng(self.get_component_info(element)['value'])
- -
[docs] def get_component_nodes(self, element: str) -> List[str]: - """ - Returns the nodes to which the component is attached to. - - :param element: Reference of the circuit element to get the nodes. - :type element: str - :return: List of nodes - :rtype: list - """ - nodes = self.get_component_info(element)['nodes'] - nodes = nodes.split( ) # Remove any spaces if they exist. This considers \r \n \t characters as well - return nodes
- -
[docs] def set_component_values(self, **kwargs): - """ - Adds one or more components on the netlist. The argument is in the form of a key-value pair where each - component designator is the key and the value is value to be set in the netlist. - - Usage 1: :: - - LTC.set_component_values(R1=330, R2="3.3k", R3="1Meg", V1="PWL(0 1 30m 1 30.001m 0 60m 0 60.001m 1)") - - Usage 2: :: - - value_settings = {'R1': 330, 'R2': '3.3k', 'R3': "1Meg", 'V1': 'PWL(0 1 30m 1 30.001m 0 60m 0 60.001m 1)'} - LTC.set_component_values(**value_settings) - - :key <comp_ref>: - The key is the component designator (Ex: V1) and the value is the value to be set. Values can either be - strings; integers or floats - - :return: Nothing - :raises: ComponentNotFoundError - In case one of the component is not found. - """ - for value in kwargs: - self.set_component_value(value, kwargs[value])
- -
[docs] def get_components(self, prefixes='*') -> list: - """ - Returns a list of components that match the list of prefixes indicated on the parameter prefixes. - In case prefixes is left empty, it returns all the ones that are defined by the REPLACE_REGEXES. - The list will contain the designators of all components found. - - :param prefixes: - Type of prefixes to search for. Examples: 'C' for capacitors; 'R' for Resistors; etc... See prefixes - in SPICE documentation for more details. - The default prefix is '*' which is a special case that returns all components. - :type prefixes: str - - :return: - A list of components matching the prefixes demanded. - """ - answer = [] - if prefixes == '*': - prefixes = ''.join(REPLACE_REGXES.keys()) - for line in self.netlist: - if isinstance(line, SpiceCircuit): # Only gets components from the main netlist, - # it currently skips sub-circuits - continue - tokens = line.split() - try: - if tokens[0][0] in prefixes: - answer.append(tokens[0]) # Appends only the designators - except IndexError or TypeError: - pass - return answer
- -
[docs] def remove_component(self, designator: str): - """ - Removes a component from the design. - Note: Current implemetation only alows removal of a component from the main netlist, not from a sub-circuit. - - :param designator: Component reference in the design. Ex: V1, C1, R1, etc... - :type designator: str - - :return: Nothing - :raises: ComponentNotFoundError - When the component doesn't exist on the netlist. - """ - line = self._getline_startingwith(designator) - self.netlist[line] = '' # Blanks the line
- -
[docs] @staticmethod - def add_library_search_paths(paths): - """ - Adding search paths for libraries. By default the local directory and the - ~username/"Documents/LTspiceXVII/lib/sub will be searched forehand. Only when a library is not found in these - paths then the paths added by this method will be searched. - Alternatively PyLTSpice.SpiceEditor.LibSearchPaths.append(paths) can be used." - - :param paths: Path to add to the Search path - :type paths: str - :return: Nothing - :rtype: None - """ - global LibSearchPaths - if isinstance(paths, str): - LibSearchPaths.append(paths) - elif isinstance(paths, list): - LibSearchPaths += paths
- -
[docs] def get_all_nodes(self) -> List[str]: - """ - A function that retrieves all nodes existing on a Netlist - - :returns: Circuit Nodes - :rtype: list[str] - """ - circuit_nodes = [] - for line in self.netlist: - prefix = get_line_command(line) - if prefix in component_replace_regexs: - match = component_replace_regexs[prefix].match(line) - if match: - nodes = match.group('nodes').split() # This separates by all space characters including \t - for node in nodes: - if node not in circuit_nodes: - circuit_nodes.append(node) - return circuit_nodes
- -
[docs]class SpiceEditor(SpiceCircuit): - """ - This class implements interfaces to manipulate SPICE netlist files. The class doesn't update the netlist file - itself. After implementing the modifications the user should call the "write_netlist" method to write a new - netlist file. - :param netlist_file: Name of the .NET file to parse - :type netlist_file: str - :param encoding: Forcing the encoding to be used on the circuit netlile read. Defaults to 'autodetect' which will - call a function that tries to detect the encoding automatically. This however is not 100% fool proof. - :type encoding: str, optional - """ - def __init__(self, netlist_file, encoding='autodetect'): - super().__init__() - self.netlist_file = netlist_file - self.modified_subcircuits = {} - if encoding == 'autodetect': - self.encoding = detect_encoding(netlist_file, '*') # Normally the file will start with a '*' - else: - self.encoding = encoding - self.reset_netlist() - - def _set_model_and_value(self, component, value): - prefix = component[0] # Using the first letter of the component to identify what is it - if prefix == 'X' and SUBCIRCUIT_DIVIDER in component: # Relaces a component inside of a subciruit - # In this case the subcircuit needs to be copied so that is copy is modified. A copy is created for each - # instance of a subcircuit. - component_split = component.split(SUBCIRCUIT_DIVIDER) - modified_path = SUBCIRCUIT_DIVIDER.join(component_split[:-1]) # excludes last component - component = component_split[-1] # This is the last component to modify - - if modified_path in self.modified_subcircuits: # See if this was already a modified subcircuit instance - sub_circuit = self.modified_subcircuits[modified_path] - else: - sub_circuit_original = self._get_subcircuit(modified_path) # If not will look of it. - if sub_circuit_original: - new_name = sub_circuit_original.name() + '_' + '_'.join(component_split[:-1]) # Creates a new name with the path appended - sub_circuit = sub_circuit_original.clone(new_name=new_name) - # Memorize that the copy is relative to that particular instance - self.modified_subcircuits[modified_path] = sub_circuit - # Change the call to the subcircuit - self._set_model_and_value(modified_path, new_name) - else: - raise ComponentNotFoundError(component) - # Change the copy of the subcircuit related to that particular instance. - sub_circuit._set_model_and_value(component, value) - return - # else: This is the generic case where the subcircuit is changed - super()._set_model_and_value(component, value) - -
[docs] def add_instruction(self, instruction: str) -> None: - """Serves to add SPICE instructions to the simulation netlist. For example: - - .. code-block:: text - - .tran 10m ; makes a transient simulation - .meas TRAN Icurr AVG I(Rs1) TRIG time=1.5ms TARG time=2.5ms" ; Establishes a measuring - .step run 1 100, 1 ; makes the simulation run 100 times - - :param instruction: - Spice instruction to add to the netlist. This instruction will be added at the end of the netlist, - typically just before the .BACKANNO statement - :type instruction: str - :return: Nothing - """ - if not instruction.endswith(END_LINE_TERM): - instruction += END_LINE_TERM - if _is_unique_instruction(instruction): - # Before adding new instruction, delete previously set unique instructions - i = 0 - while i < len(self.netlist): - line = self.netlist[i] - if _is_unique_instruction(line): - self.netlist[i] = instruction - break - else: - i += 1 - - # check whether the instruction is already there (dummy proofing) - # TODO: if adding a .MODEL or .SUBCKT it should verify if it already exists and update it. - if instruction not in self.netlist: - # Insert before backanno instruction - try: - line = self.netlist.index('.backanno\n') # TODO: Improve this. END of line termination could be differnt and case as well - except ValueError: - line = len(self.netlist) - 2 # This is where typically the .backanno instruction is - self.netlist.insert(line, instruction)
- -
[docs] def add_instructions(self, *instructions)->None: - """Adds a list of instructions to the SPICE NETLIST. - Example: - :: - - LTC.add_instructions( - ".STEP run -1 1023 1", - ".dc V1 -5 5" - ) - - :param instructions: Argument list of instructions to add - :type instructions: list - :returns: Nothing - """ - for instruction in instructions: - self.add_instruction(instruction)
- -
[docs] def remove_instruction(self, instruction)->None: - """Usage a previously added instructions. - Example: :: - - LTC.remove_instruction(".STEP run -1 1023 1") - - :param instructions The list of instructions to remove. Each instruction is of the type 'str' - :type instruction: str - :returns: Nothing - TODO: This only works with a full line instruction. Make it more inteligent so it recognizes .models, .param - and .subckt - """ - # Because the netlist is stored containing the end of line terminations and because they are added when they - # they are added to the netlist. - if not instruction.endswith(END_LINE_TERM): - instruction += END_LINE_TERM - - self.netlist.remove(instruction)
- -
[docs] def write_netlist(self, run_netlist_file: str)->None: - """ - Writes the netlist will all the requested updates into a file named <run_netlist_file>. - :param run_netlist_file: File name of the netlist file. - :type run_netlist_file: str - :return Nothing - """ - f = open(run_netlist_file, 'w', encoding=self.encoding) - lines = iter(self.netlist) - for line in lines: - if isinstance(line, SpiceCircuit): - line._write_lines(f) - else: - # Writes the modified subcircuits at the end just before the .END clause - if line.upper().startswith(".END"): - # write here the modified subcircuits - for sub in self.modified_subcircuits.values(): - sub._write_lines(f) - f.write(line) - f.close()
- -
[docs] def reset_netlist(self) -> None: - """ - Removes all previous edits done to the netlist, i.e. resets it to the original state. - - :returns: Nothing - """ - self.netlist.clear() - self.modified_subcircuits.clear() - if os.path.exists(self.netlist_file): - with open(self.netlist_file, 'r', encoding=self.encoding, errors='replace') as f: - lines = iter(f) # Creates an iterator object to consume the file - finished = self._add_lines(lines) - if not finished: - raise SyntaxError("Netlist with missing .END or .ENDS statements") - else: - for remainig_lines in lines: - print("Ignoring %s" % remainig_lines) - else: - self.logger.error("Netlist file not found")
- -
[docs] @staticmethod - def find_subckt_in_lib(library, subckt_name) -> 'SpiceEditor': - """ - Finds returns a Subckt from a library file - - :param library: path to the library to search - :type library: str - :param subckt_name: Subcircuit to search for - """ - # 0. Setup things - reg_subckt = re.compile(SUBCKT_CLAUSE_FIND + subckt_name, re.IGNORECASE) - # 1. Find Encoding - encoding = detect_encoding(library) - # 2. scan the file - with open(library, encoding=encoding) as lib: - for line in lib: - search = reg_subckt.match(line) - if search: - sub_circuit = SpiceCircuit() - sub_circuit.netlist.append(line) - # Advance to the next non nested .ENDS - finished = sub_circuit._add_lines(lib) - if finished: - return sub_circuit - # 3. Return an instance of SpiceEditor - return None
- -if __name__ == '__main__': - E = SpiceEditor(os.path.abspath('..\\tests\\PI_Filter_resampled.net')) - E.add_instruction(".nodeset V(N001)=0") - E.write_netlist('..\\tests\\PI_Filter_resampled_mod.net') - E = SpiceEditor('..\\tests\\Editor_Test.net') - print("Circuit Nodes", E.get_all_nodes()) - E.add_library_search_paths([r"C:\SVN\Electronic_Libraries\LTSpice\lib"]) - E.set_element_model("XU2", 324) - E.set_component_value("XU1:XDUT:R77", 200) - print(E.get_component_value('R1')) - print("Setting R1 to 10k") - E.set_component_value('R1', 10000) - print("Setting parameter I1 1.23k") - E.set_parameter("I1", "1.23k") - print(E.get_parameter('I1')) - print("Setting {freq*(10/5.0})") - E.set_parameters(I2="{freq*(10/5.0})") - print(E.get_parameter('I2')) - print(E.get_components()) - print(E.get_components('RC')) - print("Setting C1 to 1µF") - E.set_component_value("C1", '1µF') - print("Setting C4 to 22nF") - E.set_component_value("C4", 22e-9) - print("Setting C3 to 120nF") - E.set_component_value("C3", '120n') - print(E.get_component_floatvalue("C1")) - print(E.get_component_floatvalue("C3")) - print(E.get_component_floatvalue("C4")) - E.set_parameters( - test_exiting_param_set1=24, - test_exiting_param_set2=25, - test_exiting_param_set3=26, - test_exiting_param_set4=27, - test_add_parameter=34.45,) - E.write_netlist("..\\tests\\test_spice_editor.net") -
- -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/_modules/PyLTSpice/raw_read.html b/doc_build/html/_modules/PyLTSpice/raw_read.html deleted file mode 100644 index 17ff48d..0000000 --- a/doc_build/html/_modules/PyLTSpice/raw_read.html +++ /dev/null @@ -1,854 +0,0 @@ - - - - - - - - PyLTSpice.raw_read — PyLTSpice 3.0 documentation - - - - - - - - - - - - -
-
- -
- -
-
-
- -

Source code for PyLTSpice.raw_read

-#!/usr/bin/env python
-# coding=utf-8
-
-# -------------------------------------------------------------------------------
-#    ____        _   _____ ____        _
-#   |  _ \ _   _| | |_   _/ ___| _ __ (_) ___ ___
-#   | |_) | | | | |   | | \___ \| '_ \| |/ __/ _ \
-#   |  __/| |_| | |___| |  ___) | |_) | | (_|  __/
-#   |_|    \__, |_____|_| |____/| .__/|_|\___\___|
-#          |___/                |_|
-#
-# Name:        raw_read.py
-# Purpose:     Process LTSpice output files and align data for usage in a spread-
-#              sheet tool such as Excel, or Calc.
-#
-# Author:      Nuno Brum (nuno.brum@gmail.com)
-#
-# Created:     19-06-2022
-# Licence:     refer to the LICENSE file
-# -------------------------------------------------------------------------------
-
-"""
-This module reads data from an LTSpice RAW file.
-The main class object is the RawRead which is initialized with the filename of the RAW file to be processed.
-The object wil read the file and construct a structure of objects which can be used to access the data inside the
-RAW file.
-To understand why this is done so, in the next section follows a brief explanation of what is contained inside a RAW
-file.
-In case RAW file contains stepped data detected, i.e. when the .STEP command is used, then it will also try to open the
-simulation LOG file and read the stepping information.
-
-RAW File Structure
-==================
-
-This section is written to help understand the why the structure of classes is defined as it is. You can gladly skip
-this section and get right down to business by seeing the examples section below.
-
-The RAW file starts with a text preamble that contains information about the names of the traces the order they
-appear on the binary part and some extra information.
-In the preamble, the lines are always started by one of the following identifiers:
-
-   + Title:          => Contains the path of the source .asc file used to make the simulation preceded by *
-
-   + Date:           => Date when the simulation started
-
-   + Plotname:       => Name of the simulation. The known Simulation Types are:
-                       * Operation Point
-                       * DC transfer characteristic
-                       * AC Analysis
-                       * Transient Analysis
-                       * Noise Spectral Density - (V/Hz½ or A/Hz½)
-                       * Transfer Function
-
-   + Flags:          => Flags that are used in this plot. The simulation can have any combination of these flags.
-                      * "real" -> The traces in the raw file contain real values. As for exmple on a TRAN simulation.
-                      * "complex" -> Traces in the raw file contain complex values. As for exmple on an AC simulation.
-                      * "forward" -> Tells whether the simulation has more than one point. DC transfer
-                        characteristic, AC Analysis, Transient Analysis or Noise Spectral Density have the forward flag.
-                        Operating Point and Transfer Function don't have this flag activated.
-                      * "log" -> The preferred plot view of this data is logarithmic.
-                      * "stepped" -> The simulation had .STEP primitives.
-                      * "FastAccess" -> Order of the data is changed to speed up access. See Binary section for details.
-
-   + No. Variables:  => number of variables contained in this dataset. See section below for details.
-
-   + No. Points:     => number of points per each variable in
-
-   + Offset:         => when the saving of data started
-
-   + Command:        => Name of the simulator executable generating this file.
-
-   + Backannotation: => Backannotation alerts that occurred during simulation
-
-   + Variables:      => a list of variable, one per line as described below
-
-   + Binary:         => Start of the binary section. See section below for details.
-
-Variables List
---------------
-The variable list contains the list of measurements saved in the raw file. The order of the variables defines how they are
-stored in the binary section. The format is one variable per line, using the following format:
-
-<tab><ordinal number><tab><measurement><tab><type of measurement>
-
-Here is an example:
-
-.. code-block:: text
-
-    0	time	time
-    1	V(n001)	   voltage
-    2	V(n004)	   voltage
-    3	V(n003)	   voltage
-    4	V(n006)	   voltage
-    5	V(adcc)    voltage
-    6	V(n002)	   voltage
-    7	V(3v3_m)   voltage
-    8	V(n005)	   voltage
-    9	V(n007)	   voltage
-    10	V(24v_dsp) voltage
-    11	I(C3)	   device_current
-    12	I(C2)	   device_current
-    13	I(C1)	   device_current
-    14	I(I1)	   device_current
-    15	I(R4)	   device_current
-    16	I(R3)	   device_current
-    17	I(V2)	   device_current
-    18	I(V1)	   device_current
-    19	Ix(u1:+)   subckt_current
-    20	Ix(u1:-)   subckt_current
-
-Binary Section
---------------
-The binary section of .RAW file is where the data is usually written, unless the user had explicitly specified an ASCII
-representation. In this case this section is replaced with a "Values" section.
-LTSpice stores data directly onto the disk during simulation, writing per each time or frequency step the list of
-values, as exemplified below for a .TRAN simulation.
-
-     <timestamp 0><trace1 0><trace2 0><trace3 0>...<traceN 0>
-
-     <timestamp 1><trace1 1><trace2 1><trace3 1>...<traceN 1>
-
-     <timestamp 2><trace1 2><trace2 2><trace3 2>...<traceN 2>
-
-     ...
-
-     <timestamp T><trace1 T><trace2 T><trace3 T>...<traceN T>
-     
-Depending on the type of simulation the type of data changes.
-On TRAN simulations the timestamp is always stored as 8 bytes float (double) and trace values as a 4 bytes (single).
-On AC simulations the data is stored in complex format, which includes a real part and an imaginary part, each with 8
-bytes.
-The way we determine the size of the data is dividing the total block size by the number of points, then taking only
-the integer part.
-
-Fast Access
------------
-
-Once a simulation is done, the user can ask LTSpice to optimize the data structure in such that variables are stored
-contiguously as illustrated below.
-
-     <timestamp 0><timestamp 1>...<timestamp T>
-
-     <trace1 0><trace1 1>...<trace1 T>
-
-     <trace2 0><trace2 1>...<trace2 T>
-
-     <trace3 0><trace3 1>...<trace3 T>
-
-     ...
-
-     <traceN T><traceN T>...<tranceN T>
-
-This can speed up the data reading. Note that this transformation is not done automatically. Transforming data to Fast
-Access must be requested by the user. If the transformation is done, it is registered in the Flags: line in the
-header. RawReader supports both Normal and Fast Access formats
-
-Classes Defined
-===============
-
-The .RAW file is read during the construction (constructor method) of an `RawRead` object. All traces on the RAW
-file are uploaded into memory.
-
-The RawRead class then has all the methods that allow the user to access the Axis and Trace Values. If there is
-any stepped data (.STEP primitives), the RawRead class will try to load the log information from the same
-directory as the raw file in order to obtain the STEP information.
-
-Follows an example of the RawRead class usage. Information on the RawRead methods can be found here.
-
-Examples
-========
-
-The example below demonstrates the usage of the RawRead class. It reads a .RAW file and uses the matplotlib
-library to plot the results of three traces in two subplots. ::
-
-    import matplotlib.pyplot as plt  # Imports the matplotlib library for plotting the results
-
-    LTR = RawRead("some_random_file.raw")  # Reads the RAW file contents from file
-
-    print(LTR.get_trace_names())  # Prints the contents of the RAW file. The result is a list, and print formats it.
-    print(LTR.get_raw_property())  # Prints all the properties found in the Header section.
-
-    plt.figure()  # Creates the canvas for plotting
-
-    vin = LTR.get_trace('V(in)')  # Get's the trace data. If Numpy is installed, then it comes in numpy array format.
-    vout = LTR.get_trace('V(out)') # Get's the second trace.
-
-    steps = LTR.get_steps()  # Get's the step information. Returns a list of step numbers, ex: [0,1,2...]. If no steps
-                             # are present on the RAW file, returns only one step : [0] .
-
-    fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)  # Creates the two subplots. One on top of the other.
-
-    for ax in (ax1, ax2):  # Crates a grid on all the plots.
-        ax.grid(True)
-
-    plt.xlim([0.9e-3, 1.2e-3])  # Optionally, limits the X axis to just a subrange.
-
-    x = LTR.get_axis(0)  # Retrieves the time vector that will be used as X axis. Uses STEP 0
-    ax1.plot(x, vin.get_wave(0)) # On first plot plots the first STEP (=0) of Vin
-
-    for step in steps:  # On the second plot prints all the STEPS of the Vout
-        x = LTR.get_axis(step)  # Retrieves the time vector that will be used as X axis.
-        ax2.plot(x, vout.get_wave(step))
-
-    plt.show()  # Creates the matplotlib's interactive window with the plots.
-
-"""
-
-__author__ = "Nuno Canto Brum <nuno.brum@gmail.com>"
-__copyright__ = "Copyright 2022, Fribourg Switzerland"
-
-import os
-
-from collections import OrderedDict
-from struct import unpack
-from typing import Union, List, Tuple
-from pathlib import Path
-
-from .raw_classes import Axis, Trace, DummyTrace, SpiceReadException
-from .detect_encoding import detect_encoding
-
-import numpy as np
-from numpy import zeros, complex128, float32, float64, frombuffer, angle
-
-
-def read_float64(f):
-    """
-    Reads a 64 bit float value, normally associated with the plot X axis.
-    The codification is done as follows:
-
-    =====  === === === ===   === === === ===
-    bit#   7   6   5   4     3   2   1   0
-    =====  === === === ===   === === === ===
-    Byte7  SGM SGE E9  E8    E7  E6  E5  E4
-    Byte6  E3  E2  E1  E0    M51 M50 M49 M48
-    Byte5  M47 M46 M45 M44   M43 M42 M41 M40
-    Byte4  M39 M38 M37 M36   M35 M34 M33 M32
-    Byte3  M31 M30 M29 M28   M27 M26 M25 M24
-    Byte2  M23 M22 M21 M20   M19 M18 M17 M16
-    Byte1  M15 M14 M13 M12   M11 M10 M9  M8
-    Byte0  M7  M6  M5  M4    M3  M2  M1  M0
-    =====  === === === ===   === === === ===
-
-    Legend:
-
-    SGM - Signal of Mantissa: 0 - Positive 1 - Negative
-
-    SGE - Signal of Exponent: 0 - Positive 1 - Negative
-
-    E[9:0] - Exponent
-
-    M[51:0] - Mantissa.
-
-    :param f: data stream to convert to float value
-    :type f: file
-    :returns: double precision float
-    :rtype: float
-    """
-    s = f.read(8)
-    return unpack("d", s)[0]
-
-
-def read_complex(f):
-    """
-    Used to convert a 16 byte stream into a complex data point. Usually used for the .AC simulations.
-    The encoding is the same as for the set_pointB8() but two values are encoded. First one is the real part and
-    the second is the complex part.
-
-    :param f: data stream
-    :type f: file
-    :return: complex value
-    :rtype: complex
-    """
-    s = f.read(16)
-    (re, im) = unpack('dd', s)
-    return complex(re, im)
-
-
-def read_float32(f):
-    """
-    Reads a 32bit float (single precision) from a stream. This is how most real values are stored in the RAW file.
-    This codification uses 4 bytes as follows:
-
-    =====  === === === ===   === === === ===
-    bit#   7   6   5   4     3   2   1   0
-    =====  === === === ===   === === === ===
-    Byte3  SGM SGE E6  E5    E4  E3  E2  E1
-    Byte2  E0  M22 M21 M20   M19 M18 M17 M16
-    Byte1  M15 M14 M13 M12   M11 M10 M9  M8
-    Byte0  M7  M6  M5  M4    M3  M2  M1  M0
-    =====  === === === ===   === === === ===
-
-    Legend:
-
-    SGM - Signal of Mantissa: 0 - Positive 1 - Negative
-
-    SGE - Signal of Exponent: 0 - Positive 1 - Negative
-
-    E[6:0] - Exponent
-
-    M[22:0] - Mantissa.
-
-    :param f: data stream to read from
-    :type f: file
-    :returns: float value
-    :rtype: float
-    """
-    s = f.read(4)
-    return unpack("f", s)[0]
-
-
-def consume4bytes(f):
-    """Used to advance the file pointer 4 bytes"""
-    f.read(4)
-
-
-def consume8bytes(f):
-    """Used to advance the file pointer 8 bytes"""
-    f.read(8)
-
-
-def consume16bytes(f):
-    """Used to advance the file pointer 16 bytes"""
-    f.read(16)
-
-
-
[docs]class RawRead(object): - """Class for reading LTSpice wave Files. It can read all types of Files. If stepped data is detected, - it will also try to read the corresponding LOG file so to retrieve the stepped data. - - :param raw_filename: The file containing the RAW data to be read - :type raw_filename: str | pahtlib.Path - :param traces_to_read: - A string or a list containing the list of traces to be read. If None is provided, only the header is read and - all trace data is discarded. If a '*' wildcard is given or no parameter at all then all traces are read. - :type traces_to_read: str, list or tuple - :key headeronly: - Used to only load the header information and skip the trace data entirely. Use `headeronly=True`. - """ - header_lines = ( - "Title", - "Date", - "Plotname", - "Output", - "Flags", - "No. Variables", - "No. Points", - "Offset", - "Command", - "Variables", - "Backannotation" - ) - - ACCEPTED_PLOTNAMES = ( - 'AC Analysis', - 'DC transfer characteristic', - 'Operating Point', - 'Transient Analysis', - 'Transfer Function', - 'Noise Spectral Density', - ) - - def __init__(self, raw_filename: str, traces_to_read: Union[str, List[str], Tuple[str], None] = '*', **kwargs): - self.verbose = kwargs.get('verbose', True) - raw_filename = Path(raw_filename) - if traces_to_read is not None: - assert isinstance(traces_to_read, (str, list, tuple)), "traces_to_read must be a string, a list or None" - - raw_file_size = os.stat(raw_filename).st_size # Get the file size in order to know the data size - raw_file = open(raw_filename, "rb") - - ch = raw_file.read(6) - if ch.decode(encoding='utf_8') == 'Title:': - self.encoding = 'utf_8' - sz_enc = 1 - line = 'Title:' - elif ch.decode(encoding='utf_16_le') == 'Tit': - self.encoding = 'utf_16_le' - sz_enc = 2 - line = 'Tit' - else: - raise RuntimeError("Unrecognized encoding") - if self.verbose: - print("Reading file with encoding ", self.encoding) - # Storing the filename as part of the dictionary - self.raw_params = OrderedDict(Filename=raw_filename) # Initializing the dict that contains all raw file info - self.backannotations = [] # Storing backannotations - header = [] - binary_start = 6 - line = "" - while True: - ch = raw_file.read(sz_enc).decode(encoding=self.encoding) - binary_start += sz_enc - if ch == '\n': - if self.encoding == 'utf_8': # must remove the \r - line = line.rstrip('\r') - header.append(line) - if line in ('Binary:', 'Values:'): - self.raw_type = line - break - line = "" - else: - line += ch - for line in header: - k, _, v = line.partition(':') - if k == 'Variables': - break - self.raw_params[k] = v.strip() - self.nPoints = int(self.raw_params['No. Points'], 10) - self.nVariables = int(self.raw_params['No. Variables'], 10) - - has_axis = self.raw_params['Plotname'] not in ('Operating Point', 'Transfer Function',) - - self._traces = [] - self.steps = None - self.axis = None # Creating the axis - self.flags = self.raw_params['Flags'].split() - if 'complex' in self.raw_params['Flags'] or self.raw_params['Plotname'] == 'AC Analysis': - numerical_type = 'complex' - else: - numerical_type = 'real' - i = header.index('Variables:') - ivar = 0 - for line in header[i + 1:-1]: # Parse the variable names - _, name, var_type = line.lstrip().split('\t') - if has_axis and ivar == 0: # If it has an axis, it should be always read - if numerical_type == 'real': - axis_numerical_type = 'double' - else: - axis_numerical_type = numerical_type - self.axis = Axis(name, var_type, self.nPoints, axis_numerical_type) - trace = self.axis - elif (traces_to_read == "*") or (name in traces_to_read): - if has_axis: # Reads data - trace = Trace(name, var_type, self.nPoints, self.axis, numerical_type) - else: - # If an Operation Point or Transfer Function, only one point per step - trace = Trace(name, var_type, self.nPoints, None, 'real') - else: - trace = DummyTrace(name, var_type) - - self._traces.append(trace) - ivar += 1 - - if traces_to_read is None or len(self._traces) == 0: - # The read is stopped here if there is nothing to read. - raw_file.close() - return - - if kwargs.get("headeronly", False): - raw_file.close() - return - - if self.verbose: - print("File contains {} traces, reading {}".format(ivar, len(self._traces))) - - if self.raw_type == "Binary:": - # Will start the reading of binary values - # But first check whether how data is stored. - self.block_size = (raw_file_size - binary_start) // self.nPoints - self.data_size = self.block_size // self.nVariables - - scan_functions = [] - for trace in self._traces: - if self.data_size == 8: - if isinstance(trace, DummyTrace): - fun = consume8bytes - else: - fun = read_float64 - elif self.data_size == 16: - if isinstance(trace, DummyTrace): - fun = consume16bytes - else: - fun = read_complex - else: # data size is only 4 bytes - if len(scan_functions) == 0: # This is the axis - fun = read_float64 - else: - if isinstance(trace, DummyTrace): - fun = consume4bytes - else: - fun = read_float32 - scan_functions.append(fun) - - if "fastaccess" in self.raw_params["Flags"]: - if self.verbose: - print("Binary RAW file with Fast access") - # Fast access means that the traces are grouped together. - for i, var in enumerate(self._traces): - if isinstance(var, DummyTrace): - # TODO: replace this by a seek - raw_file.read(self.nPoints * self.data_size) - else: - if self.data_size == 8: - s = raw_file.read(self.nPoints * 8) - var.data = frombuffer(s, dtype=float64) - elif self.data_size == 16: - s = raw_file.read(self.nPoints * 16) - var.data = frombuffer(s, dtype=complex) - else: - if i == 0: - s = raw_file.read(self.nPoints * 8) - var.data = frombuffer(s, dtype=float64) - else: - s = raw_file.read(self.nPoints * 4) - var.data = frombuffer(s, dtype=float32) - - else: - if self.verbose: - print("Binary RAW file with Normal access") - # This is the default save after a simulation where the traces are scattered - for point in range(self.nPoints): - for i, var in enumerate(self._traces): - value = scan_functions[i](raw_file) - if value is not None: - var.data[point] = value - - elif self.raw_type == "Values:": - if self.verbose: - print("ASCII RAW File") - # Will start the reading of ASCII Values - for point in range(self.nPoints): - first_var = True - for var in self._traces: - line = raw_file.readline().decode(encoding=self.encoding, errors='ignore') - if first_var: - first_var = False - spoint = line.split("\t", 1)[0] - - if point != int(spoint): - print("Error Reading File") - break - value = line[len(spoint):-1] - else: - value = line[:-1] - if not isinstance(var, DummyTrace): - var.data[point] = float(value) - else: - raw_file.close() - raise SpiceReadException("Unsupported RAW File. ""%s""" % self.raw_type) - - raw_file.close() - - # Setting the properties in the proper format - self.raw_params["No. Points"] = self.nPoints - self.raw_params["No. Variables"] = self.nVariables - self.raw_params["Variables"] = [var.name for var in self._traces] - # Now Purging Dummy Traces - i = 0 - while i < len(self._traces): - if isinstance(self._traces[i], DummyTrace): - del self._traces[i] - else: - i += 1 - - # Finally, Check for Step Information - if "stepped" in self.raw_params["Flags"]: - try: - self._load_step_information(raw_filename) - except SpiceReadException: - print("LOG file not found or problems happened while reading it. Auto-detecting steps") - if has_axis: - number_of_steps = 0 - for v in self.axis: - if v == self.axis[0]: - number_of_steps += 1 - else: - number_of_steps = self.nPoints - self.steps = [{'run': i+1} for i in range(number_of_steps)] - - if self.steps is not None: - if has_axis: - # Individual access to the Trace Classes, this information is stored in the Axis - # which is always in position 0 - self._traces[0]._set_steps(self.steps) - -
[docs] def get_raw_property(self, property_name=None): - """ - Get a property. By default it returns all properties defined in the RAW file. - - :param property_name: name of the property to retrieve. - :type property_name: str - :returns: Property object - :rtype: str - :raises: ValueError if the property doesn't exist - """ - if property_name is None: - return self.raw_params - elif property_name in self.raw_params.keys(): - return self.raw_params[property_name] - else: - raise ValueError("Invalid property. Use %s" % str(self.raw_params.keys()))
- -
[docs] def get_trace_names(self): - """ - Returns a list of exiting trace names inside of the RAW file. - - :return: trace names - :rtype: list[str] - """ - return [trace.name for trace in self._traces]
- -
[docs] def get_trace(self, trace_ref: Union[str, int]): - """ - Retrieves the trace with the requested name (trace_ref). - - :param trace_ref: Name of the trace or the index of the trace - :type trace_ref: str or int - :return: An object containing the requested trace - :rtype: DataSet subclass - :raises IndexError: When a trace is not found - """ - if isinstance(trace_ref, str): - for trace in self._traces: - if trace_ref.upper() == trace.name.upper(): # The trace names are case insensitive - # assert isinstance(trace, DataSet) - return trace - raise IndexError(f"{self} doesn't contain trace \"{trace_ref}\"\n" - f"Valid traces are {[trc.name for trc in self._traces]}") - else: - return self._traces[trace_ref]
- -
[docs] def get_wave(self, trace_ref: Union[str, int], step: int = 0): - """ - Retrieves the trace data with the requested name (trace_ref), optionally providing the step number. - - :param trace_ref: Name of the trace or the index of the trace - :type trace_ref: str or int - :param step: Optional parameter specifying which step to retrieve. - :type step: int - :return: A numpy array containing the requested waveform. - :rtype: numpy.array - :raises IndexError: When a trace is not found - """ - return self.get_trace(trace_ref).get_wave(step)
- -
[docs] def get_time_axis(self, step: int = 0): - """ - *(Deprecated)* Use get_axis method instead - - This function is equivalent to get_trace('time').get_time_axis(step) instruction. - It's workaround on a LTSpice issue when using 2nd Order compression, where some values on - the time trace have a negative value.""" - return self.get_trace('time').get_time_axis(step)
- -
[docs] def get_axis(self, step: int = 0): - """ - This function is equivalent to get_trace(0).get_wave(step) instruction. - It also implements a workaround on a LTSpice issue when using 2nd Order compression, where some values on - the time trace have a negative value. - :param step: Step number - :type step: int - :returns: Array with the X axis - :rtype: list[float] or numpy.array - """ - if self.axis: - axis = self.get_trace(0) - if axis.whattype == 'time': - return axis.get_time_axis(step) - else: - return axis.get_wave(step) - else: - raise RuntimeError("This RAW file does not have an axis.")
- -
[docs] def get_len(self, step: int = 0) -> int: - """ - Returns the length of the data at the give step index. - :param step: Optional parameter the step index. - :type step: int - :return: The number of data points - :rtype: int - """ - return self.axis.get_len(step)
- - def _load_step_information(self, filename: Path): - # Find the extension of the file - if not filename.suffix == ".raw": - raise SpiceReadException("Invalid Filename. The file should end with '.raw'") - logfile = filename.with_suffix(".log") - try: - encoding = detect_encoding(logfile, "Circuit:") - log = open(logfile, 'r', errors='replace', encoding=encoding) - except OSError: - raise SpiceReadException("Step information needs the '.log' file generated by LTSpice") - except UnicodeError: - raise SpiceReadException("Unable to parse log file and obtain .STEP information. Check Unicode") - - for line in log: - if line.startswith(".step"): - step_dict = {} - for tok in line[6:-1].split(' '): - key, value = tok.split('=') - try: - # Tries to convert to float for backward compatibility - value = float(value) - except: - pass - # Leave value as a string to accommodate cases like temperature steps. - # Temperature steps have the form '.step temp=25°C' - step_dict[key] = value - if self.steps is None: - self.steps = [step_dict] - else: - self.steps.append(step_dict) - log.close() - - def __getitem__(self, item): - """Helper function to access traces by using the [ ] operator.""" - return self.get_trace(item) - -
[docs] def get_steps(self, **kwargs): - """Returns the steps that correspond to the query set in the * * kwargs parameters. - Example: :: - - raw_read.get_steps(V5=1.2, TEMP=25) - - This will return all steps in which the voltage source V5 was set to 1.2V and the TEMP parameter is 24 degrees. - This feature is only possible if a .log file with the same name as the .raw file exists in the same directory. - Note: the correspondence between step numbers and .STEP information is stored on the .log file. - - :key kwargs: - key-value arguments in which the key correspond to a stepped parameter or source name, and the value is the - stepped value. - - :return: The steps that match the query - :rtype: list[int] - """ - if self.steps is None: - return [0] # returns an single step - else: - if len(kwargs) > 0: - ret_steps = [] # Initializing an empty array - i = 0 - for step_dict in self.steps: - for key in kwargs: - ll = step_dict.get(key, None) - if ll is None: - break - elif kwargs[key] != ll: - break - else: - ret_steps.append(i) # All the step parameters match - i += 1 - return ret_steps - else: - return range(len(self.steps)) # Returns all the steps
- - -# Backward compatibility naming -LTSpiceRawRead = RawRead - -# if __name__ == "__main__": -# out = open("RAW_TEST_out_test1.txt", 'w') -# -# for step in LTR.get_steps(): -# for x in range(len(LTR[0].data)): -# out.write("%s, %e, %e\n" % (step, LTR[0].data[x], LTR[2].data[x])) -# out.close() -
- -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/_modules/PyLTSpice/raw_write.html b/doc_build/html/_modules/PyLTSpice/raw_write.html deleted file mode 100644 index c553f16..0000000 --- a/doc_build/html/_modules/PyLTSpice/raw_write.html +++ /dev/null @@ -1,563 +0,0 @@ - - - - - - - - PyLTSpice.raw_write — PyLTSpice 3.0 documentation - - - - - - - - - - - - -
-
- -
- -
-
-
- -

Source code for PyLTSpice.raw_write

-#!/usr/bin/env python
-# coding=utf-8
-
-# -------------------------------------------------------------------------------
-#    ____        _   _____ ____        _
-#   |  _ \ _   _| | |_   _/ ___| _ __ (_) ___ ___
-#   | |_) | | | | |   | | \___ \| '_ \| |/ __/ _ \
-#   |  __/| |_| | |___| |  ___) | |_) | | (_|  __/
-#   |_|    \__, |_____|_| |____/| .__/|_|\___\___|
-#          |___/                |_|
-#
-# Name:        raw_write.py
-# Purpose:     Create RAW Files
-#
-# Author:      Nuno Brum (nuno.brum@gmail.com)
-#
-# Created:     16-10-2021
-# Licence:     refer to the LICENSE file
-# -------------------------------------------------------------------------------
-
-"""
-This module generates RAW Files from user data.
-It can be used to combine RAW files generated by different Simulation Runs
-"""
-from typing import Union
-from time import strftime
-
-from .raw_read import RawRead
-from .raw_classes import DataSet
-from numpy import array, float32, zeros
-
-
-class Trace(DataSet):
-    """Helper class representing a trace. This class is based on DataSet, therefore, it doesn't support STEPPED data.
-    :param name: name of the trace being created
-    :type name: str
-    :param whattype: time, frequency, voltage or current
-    :type whattype: str
-    :param data: data for the data write
-    :type data: list or numpy.array
-    :param numerical_type: real or complex
-    :type numerical_type: str
-    """
-
-    def __init__(self, name, data, whattype='voltage', numerical_type=''):
-        if name == 'time':
-            whattype = 'time'
-        elif name == 'frequency':
-            whattype = 'frequency'
-        if numerical_type == '':
-            if name == 'time':
-                numerical_type = 'double'
-            elif name == 'frequency':
-                numerical_type = 'complex'
-            elif isinstance(data[0], float32) or isinstance(data[0], float):
-                numerical_type = 'real'
-            elif isinstance(data[0], complex):
-                numerical_type = 'complex'
-            else:
-                raise NotImplementedError
-
-        DataSet.__init__(self, name, whattype, len(data), numerical_type=numerical_type)
-        if isinstance(data, (list, tuple)):
-            self.data = array(data, dtype=self.data.dtype)
-        else:
-            self.data[:] = data[:]  # This way the dtype is kept
-
-
-
[docs]class RawWrite(object): - """ - This class represents the RAW data file being generated. Contrary to the RawRead this class doesn't support stepped - data. - - """ - - def __init__(self, plot_name=None, fastacces=True, numtype='real', encoding='utf_16_le'): - self._traces = list() - self.flag_numtype = numtype - self.flag_forward = False - self.flag_log = False - self.flag_stepped = False - self.flag_fastaccess = fastacces - self.plot_name = plot_name - self.offset = 0.0 - self.encoding = encoding - self._imported_data = [] - self._new_axis = None - - def _str_flags(self): - flags = [self.flag_numtype] - if self.flag_forward: - flags.append('forward') - if self.flag_log: - flags.append('log') - if self.flag_stepped: - flags.append('stepped') - if self.flag_fastaccess: - flags.append('fastaccess') - return ' '.join(flags) - -
[docs] def add_trace(self, trace: Trace): - """ - Adds a trace to the RAW file. The trace needs to have the same size as trace 0 ('time', 'frequency', etc..) - The first trace added defines the X-Axis and therefore the type of RAW file being generated. If no plot name - was defined, it will automatically assign a name. - :param trace: Needs to be of the - :type trace: - :return: Nothing - :rtype: None - """ - assert isinstance(trace, Trace), "The trace needs to be of the type ""Trace""" - if self.plot_name is None or len(self._traces) == 0: - if trace.whattype == 'time': - self.plot_name = self.plot_name or 'Transient Analysis' - self.flag_numtype = 'real' - elif trace.whattype == 'frequency': - self.plot_name = self.plot_name or 'AC Analysis' - self.flag_numtype = 'complex' - elif trace.whattype in ('voltage', 'current'): - self.plot_name = self.plot_name or 'DC transfer characteristic' - self.flag_numtype = 'real' - elif trace.whattype == 'param': - self.plot_name = self.plot_name or 'Operating Point' - self.flag_numtype = 'real' - else: - raise ValueError("First Trace needs to be either 'time', 'frequency', 'param', 'voltage' or '...'") - else: - if len(self._traces[0]) != len(trace): - raise IndexError("The trace needs to be the same size as trace 0") - self._traces.append(trace)
- -
[docs] def save(self, filename: str): - """ - Saves the RAW file into a file. The file format is always binary. Text based RAW output format is not supported - in this version. - :param filename: filename to where the RAW file is going to be written. Make sure that the extension of the - file is .RAW. - - :type filename: str or pathlib.Path - :return: Nothing - :rtype: None - """ - if len(self._imported_data): - self._consolidate() - f = open(filename, 'wb') - f.write("Title: * PyLTSpice RawWrite\n".encode(self.encoding)) - f.write("Date: {}\n".format(strftime("%a %b %d %H:%M:%S %Y")).encode(self.encoding)) - f.write("Plotname: {}\n".format(self.plot_name).encode(self.encoding)) - f.write("Flags: {}\n".format(self._str_flags()).encode(self.encoding)) - f.write("No. Variables: {}\n".format(len(self._traces)).encode(self.encoding)) - f.write("No. Points: {:12}\n".format(len(self._traces[0])).encode(self.encoding)) - f.write("Offset: {:.16e}\n".format(self.offset).encode(self.encoding)) - f.write("Command: Linear Technology Corporation LTspice XVII\n".encode(self.encoding)) - # f.write("Backannotation: \n".encode(self.encoding)) - f.write("Variables:\n".encode(self.encoding)) - for i, trace in enumerate(self._traces): - f.write("\t{0}\t{1}\t{2}\n".format(i, trace.name, trace.whattype).encode(self.encoding)) - total_bytes = 0 - f.write("Binary:\n".encode(self.encoding)) - if self.flag_fastaccess and self.flag_numtype != 'complex': # Don't know why, but complex RAW files aren't - # converted to FastAccess - for trace in self._traces: - f.write(trace.data.tobytes()) - else: - fmts = {trace: tobytes_for_trace(trace) for trace in self._traces} - for i in range(len(self._traces[0])): - for trace in self._traces: - total_bytes += f.write(fmts[trace](trace.data[i])) - f.close()
- - @staticmethod - def _rename_netlabel(name, **kwargs) -> str: - """Renames a trace name making sure that the V() or I() containers are left intact.""" - # Make the rename as requested - if 'rename_format' in kwargs: - if name.endswith(')') and name.startswith('V(') or name.startswith('I('): - new_name = name[:2] + kwargs['rename_format'].format(name[2:-1], **kwargs) + name[-1] - else: - new_name = kwargs['rename_format'].format(name, **kwargs) - return new_name - else: - return name - - def _name_exists(self, name: str) -> bool: - # first check whether it is a duplicate - for trace in self._traces: - if trace.name == name: - return True - return False - -
[docs] def add_traces_from_raw(self, other: RawRead, trace_filter: Union[list, tuple, str], **kwargs): - """ *(Not fully implemented)* - - Merge two RawWrite classes together resulting in a new instance - :param other: an instance of the RawRead class where the traces are going to be copied from. - :type other: RawRead - :param trace_filter: A list of signals that should be imported into the new file - :type trace_filter: list, Tuple, or just a string for one trace - - :keyword force_axis_alignment: If two raw files don't have the same axis, an attempt is made to sync the two - - :keyword admissible_error: maximum error allowed in the sync between the two axis - - :keyword rename_format: when adding traces with the same name, it is possible to define a rename format. - For example, if there are two traces named N001 in order to avoid duplicate names the rename format can be - defined as "{}_{kwarg_name} where kwarg_name is passed as a keyword argument of this function. If just one - trace is being added, this can be used to simply give the new name. - - :keyword step: by default only step 0 is added from the second raw. It is possible to add other steps, by - using this keyword parameter. This is useful when we want to "flatten" the multiple step runs into the same - view. - - :keyword: minimum_timestep: This parameter forces the two axis to sync using a minimum time step. That is, all - time increments that are less than this parameter will be suppressed. - - :returns: Nothing - """ - force_axis_alignment = kwargs.get('force_axis_alignment', False) - admissible_error = kwargs.get('admissible_error', 1e-11) - from_step = kwargs.get('step', 0) - minimum_timestep = kwargs.get('minimum_timestep', 0.0) - if isinstance(trace_filter, str): - trace_filter = [trace_filter] - - other_flags = other.get_raw_property('Flags').split(' ') - for flag in other_flags: - if flag in ('real', 'complex'): - other_flag_num_type = flag - break - else: - other_flag_num_type = 'real' - - if len(self._traces): # there are already traces - if self.flag_numtype != other_flag_num_type: - raise ValueError("The two instances should have the same type:\n" - f"Source is {other_flag_num_type} and Destination is {self.flag_numtype}") - if self._traces[0].whattype != other.get_trace(0).whattype: - raise ValueError("The two instances should have the same axis type:\n" - f"Source is {other.get_trace(0).whattype} and Destination is {self._traces[0].whattype}") - if len(trace_filter) == 0: - return # There is nothing to add stop here - - else: # No traces are present - # if no X axis is present, copy from the first one - self.flag_numtype = other_flag_num_type - self.flag_log = 'log' in other_flags - self.flag_forward = 'forward' in other_flags - self.plot_name = other.get_raw_property('Plotname') - oaxis = other.get_trace(0) - new_axis = Trace(oaxis.name, other.get_axis(from_step), oaxis.whattype, oaxis.numerical_type) - self._traces.append(new_axis) - force_axis_alignment = False - - if force_axis_alignment or minimum_timestep > 0.0: - if self._new_axis: - my_axis = self._new_axis - else: - my_axis = self._traces[0].get_wave() - other_axis = other.get_axis(from_step) - new_axis = [] - if minimum_timestep > 0.0: - raise NotImplementedError - else: - i = 0 # incomming data counter - e = 0 # existing data counter - - while e < len(my_axis)-1 and i < len(other_axis)-1: - error = other_axis[i] - my_axis[e] - if abs(error) < admissible_error: - new_axis.append(my_axis[e]) - i += 1 - e += 1 - elif error < 0: - # Other axis is smaller - new_axis.append(other_axis[i]) - i += 1 - else: - new_axis.append(my_axis[e]) - e += 1 - # Creating the New Axis - self._new_axis = new_axis - - for trace_name in trace_filter: - imported_trace = other.get_trace(trace_name) - new_name = self._rename_netlabel(trace_name, **kwargs) - imported_trace.name = new_name - self._imported_data.append(imported_trace) - else: - assert len(self._traces[0]) == len(other.get_axis(from_step)), \ - "The two instances should have the same size. To avoid this use force_axis_alignment=True option" - for trace_name in trace_filter: - trace = other.get_trace(trace_name) - new_name = self._rename_netlabel(trace_name, **kwargs) - data = trace.get_wave(from_step) - self._traces.append(Trace(new_name, data, trace.whattype, numerical_type=trace.numerical_type))
- - @staticmethod - def _interpolate(trace_data, trace_axis, new_axis: array): - new_data = zeros(len(new_axis), dtype=trace_data.dtype) - new_data[0] = trace_data[0] - - slope = (trace_data[1] - trace_data[0])/(trace_axis[1] - trace_axis[0]) - i = 1 - for j, t in enumerate(new_axis): - while trace_axis[i] < t: - i += 1 - slope = (trace_data[i] - trace_data[i-1])/(trace_axis[i] - trace_axis[i-1]) - new_data[j] = trace_data[i-1] + slope * (t - trace_axis[i-1]) - return new_data - - def _consolidate(self): - if self._new_axis and self._imported_data: - new_axis = self._new_axis - axis_length = len(new_axis) - old_axis = self._traces[0] - if axis_length != len(old_axis): - my_axis = old_axis.data - for trace in self._traces[1:]: - trace.data = self._interpolate(trace.data, my_axis, new_axis) - for imported_trace in self._imported_data: - new_trace = Trace(imported_trace.name, - self._interpolate(imported_trace.data, imported_trace.axis, new_axis), - imported_trace.whattype, imported_trace.numerical_type) - self._traces.append(new_trace) - self._traces[0] = Trace(old_axis.name, new_axis, - old_axis.whattype, old_axis.numerical_type) # Replaces with the new axis - self._new_axis = None - self._imported_data.clear() - -
[docs] def get_trace(self, trace_ref): - """ - Retrieves the trace with the requested name (trace_ref). - - :param trace_ref: Name of the trace - :type trace_ref: str - :return: An object containing the requested trace - :rtype: DataSet subclass - """ - if isinstance(trace_ref, str): - for trace in self._traces: - if trace_ref == trace.name: - # assert isinstance(trace, DataSet) - return trace - raise IndexError(f"{self} doesn't contain trace \"{trace_ref}\"\n" - f"Valid traces are {[trc.name for trc in self._traces]}") - else: - return self._traces[trace_ref]
- - def __getitem__(self, item): - """Helper function to access traces by using the [ ] operator.""" - return self.get_trace(item)
- - -def tobytes_for_trace(trace: Trace): - def tobytes(value): - return value.tobytes() - return tobytes - - -if __name__ == '__main__': - import numpy as np - from raw_read import RawRead - - def test_readme_snippet(): - 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("teste_snippet1.raw") - - def test_trc2raw(): # Convert Teledyne-Lecroy trace files to raw files - f = open(r"Current_Lock_Front_Right_8V.trc") - raw_type = '' # Initialization of parameters that need to be overridden by the file header - wave_size = 0 - for line in f: - tokens = line.rstrip('\r\n').split(',') - if len(tokens) == 4: - if tokens[0] == 'Segments' and tokens[2] == 'SegmentSize': - wave_size = int(tokens[1]) * int(tokens[3]) - if len(tokens) == 2: - if tokens[0] == 'Time' and tokens[1] == 'Ampl': - raw_type = 'transient' - break - if raw_type == 'transient' and wave_size > 0: - data = np.genfromtxt(f, dtype='float,float', delimiter=',', max_rows=wave_size) - LW = RawWrite() - LW.add_trace(Trace('time', [x[0] for x in data])) - LW.add_trace(Trace('Ampl', [x[1] for x in data])) - LW.save("teste_trc.raw") - f.close() - - - def test_axis_sync(): # Test axis sync - LW = RawWrite() - 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("teste_w.raw") - LR = RawRead("..\\test_files\\testfile.raw") - LW.add_traces_from_raw(LR, ('V(out)',), force_axis_alignment=True) - LW.save("merge.raw") - test = """ - equal = True - for ii in range(len(tx)): - if t[ii] != tx[ii]: - print(t[ii], tx[ii]) - equal = False - print(equal) - - v = LR.get_trace('N001') - max_error = 1.5e-12 - for ii in range(len(vy)): - err = abs(v[ii] - vy[ii]) - if err > max_error: - max_error = err - print(v[ii], vy[ii], v[ii] - vy[ii]) - print(max_error) - """ - - def test_write_ac(): - LW = RawWrite() - LR = RawRead("..\\tests\\PI_Filter.raw") - LR1 = RawRead("..\\tests\\PI_Filter_resampled.raw") - LW.add_traces_from_raw(LR, ('V(N002)',)) - LW.add_traces_from_raw(LR1, 'V(N002)', rename_format='N002_resampled', force_axis_alignment=True) - LW.flag_fastaccess = False - LW.save("..\\tests\\PI_filter_rewritten.raw") - LW.flag_fastaccess = True - LW.save("..\\tests\\PI_filter_rewritten_fast.raw") - - def test_write_tran(): - LR = RawRead("..\\tests\\TRAN - STEP.raw") - LW = RawWrite() - LW.add_traces_from_raw(LR, ('V(out)', 'I(C1)')) - LW.flag_fastaccess = False - LW.save("..\\tests\\TRAN - STEP0_normal.raw") - LW.flag_fastaccess = True - LW.save("..\\tests\\TRAN - STEP0_fast.raw") - - def test_combine_tran(): - LW = RawWrite() - for tag, raw in ( - ("AD820_15", "../tests/Batch_Test_AD820_15.raw"), - # ("AD820_10", "../tests/Batch_Test_AD820_10.raw"), - ("AD712_15", "../tests/Batch_Test_AD712_15.raw"), - # ("AD712_10", "../tests/Batch_Test_AD712_10.raw"), - # ("AD820_5", "../tests/Batch_Test_AD820_5.raw"), - # ("AD712_5", "../tests/Batch_Test_AD712_5.raw"), - ): - LR = RawRead(raw) - LW.add_traces_from_raw(LR, ("V(out)", "I(R1)"), rename_format="{}_{tag}", tag=tag, force_axis_alignment=True) - LW.flag_fastaccess = False - LW.save("../tests/Batch_Test_Combine.raw") - - - # test_readme_snippet() - # test_axis_sync() - # test_write_ac() - # test_write_tran() - test_combine_tran() -
- -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/_modules/PyLTSpice/sim_batch.html b/doc_build/html/_modules/PyLTSpice/sim_batch.html deleted file mode 100644 index 2ab4ea5..0000000 --- a/doc_build/html/_modules/PyLTSpice/sim_batch.html +++ /dev/null @@ -1,588 +0,0 @@ - - - - - - - - PyLTSpice.sim_batch — PyLTSpice 3.0 documentation - - - - - - - - - - - - -
-
- -
- -
-
-
- -

Source code for PyLTSpice.sim_batch

-#!/usr/bin/env python
-# coding=utf-8
-
-# -------------------------------------------------------------------------------
-#    ____        _   _____ ____        _
-#   |  _ \ _   _| | |_   _/ ___| _ __ (_) ___ ___
-#   | |_) | | | | |   | | \___ \| '_ \| |/ __/ _ \
-#   |  __/| |_| | |___| |  ___) | |_) | | (_|  __/
-#   |_|    \__, |_____|_| |____/| .__/|_|\___\___|
-#          |___/                |_|
-#
-# Name:        sim_batch.py
-# Purpose:     Tool used to launch LTSpice simulation in batch mode. Netlsts can
-#              be updated by user instructions
-#
-# Author:      Nuno Brum (nuno.brum@gmail.com)
-#
-# Created:     23-12-2016
-# Licence:     refer to the LICENSE file
-# -------------------------------------------------------------------------------
-"""
-Allows launching LTSpice simulations from a Python Script, thus allowing to overcome the 3 dimensions STEP limitation on
-LTSpice, update resistor values, or component models.
-
-The code snipped below will simulate a circuit with two different diode models, set the simulation
-temperature to 80 degrees, and update the values of R1 and R2 to 3.3k. ::
-
-    LTC = SimCommander("my_circuit.asc")
-    LTC.set_parameters(temp=80)  # Sets the simulation temperature to be 80 degrees
-    LTC.set_component_value('R2', '3.3k')  #  Updates the resistor R2 value to be 3.3k
-    for dmodel in ("BAT54", "BAT46WJ"):
-        LTC.set_element_model("D1", model)  # Sets the Diode D1 model
-        for res_value in sweep(2.2, 2,4, 0.2):  # Steps from 2.2 to 2.4 with 0.2 increments
-            LTC.set_component_value('R1', res_value)  #  Updates the resistor R1 value to be 3.3k
-            LTC.run()
-
-    LTC.wait_completion()  # Waits for the LTSpice simulations to complete
-
-    print("Total Simulations: {}".format(LTC.runno))
-    print("Successful Simulations: {}".format(LTC.okSim))
-    print("Failed Simulations: {}".format(LTC.failSim))
-
-The first line will create an python class instance that represents the LTSpice file or netlist that is to be
-simulated. This object implements methods that are used to manipulate the spice netlist. For example, the method
-set_parameters() will set or update existing parameters defined in the netlist. The method set_component_value() is
-used to update existing component values or models.
-
----------------
-Multiprocessing
----------------
-
-For making better use of today's computer capabilities, the SimCommander spawns several LTSpice instances
-each executing in parallel a simulation.
-
-By default, the number of parallel simulations is 4, however the user can override this in two ways. Either
-using the class constructor argument ``parallel_sims`` or by forcing the allocation of more processes in the
-run() call by setting ``wait_resource=False``. ::
-
-    LTC.run(wait_resource=False)
-
-The recommended way is to set the parameter ``parallel_sims`` in the class constructor. ::
-
-    LTC=SimCommander("my_circuit.asc", parallel_sims=8)
-
-The user then can launch a simulation with the updates done to the netlist by calling the run() method. Since the
-processes are not executed right away, but rather just scheduled for simulation, the wait_completion() function is
-needed if the user wants to execute code only after the completion of all scheduled simulations.
-
-The usage of wait_completion() is optional. Just note that the script will only end when all the scheduled tasks are
-executed.
-
----------
-Callbacks
----------
-
-As seen above, the `wait_completion()` can be used to wait for all the simulations to be finished. However, this is
-not efficient from a multiprocessor point of view. Ideally, the post-processing should be also handled while other
-simulations are still running. For this purpose, the user can use a function call back.
-
-The callback function is called when the simulation has finished directly by the thread that has handling the
-simulation. A function callback receives two arguments.
-The RAW file and the LOG file names. Below is an example of a callback function::
-
-    def processing_data(raw_filename, log_filename):
-        '''This is a call back function that just prints the filenames'''
-        print("Simulation Raw file is %s. The log is %s" % (raw_filename, log_filename)
-        # Other code below either using LTSteps.py or raw_read.py
-        log_info = LTSpiceLogReader(log_filename)
-        log_info.read_measures()
-        rise, measures = log_info.dataset["rise_time"]
-
-The callback function is optional. If  no callback function is given, the thread is terminated just after the
-simulation is finished.
-"""
-__author__ = "Nuno Canto Brum <nuno.brum@gmail.com>"
-__copyright__ = "Copyright 2020, Fribourg Switzerland"
-
-import logging
-import os
-import pathlib
-import threading
-import time
-import traceback
-from time import sleep
-from typing import Callable, Union, Any, Tuple
-from warnings import warn
-
-from .SpiceEditor import SpiceEditor
-from .simulator import clock_function, Simulator
-
-END_LINE_TERM = '\n'
-
-logging.basicConfig(filename='SpiceBatch.log', level=logging.INFO)
-
-
-
-
-class RunTask(threading.Thread):
-    """This is an internal Class and should not be used directly by the User."""
-
-    def __init__(self, simulator: Simulator,  run_no, netlist_file: str, callback: Callable[[str, str], Any], timeout=None, verbose=True):
-        self.verbose = verbose
-        self.timeout = timeout  # Thanks to Daniel Phili for implementing this
-
-        threading.Thread.__init__(self)
-        self.setName("sim%d" % run_no)
-        self.simulator = simulator
-        self.run_no = run_no
-        self.netlist_file = netlist_file
-        self.callback = callback
-        self.retcode = -1  # Signals an error by default
-        self.raw_file = None
-        self.log_file = None
-
-    def run(self):
-        # Setting up
-        logger = logging.getLogger("sim%d" % self.run_no)
-        logger.setLevel(logging.INFO)
-
-        # Running the Simulation
-
-        self.start_time = clock_function()
-        if self.verbose:
-            print(time.asctime(), ": Starting simulation %d" % self.run_no)
-
-        # start execution
-        self.retcode = self.simulator.run(self.netlist_file, self.timeout)
-
-        # print simulation time
-        sim_time = time.strftime("%H:%M:%S", time.gmtime(clock_function() - self.start_time))
-        netlist_radic, extension = os.path.splitext(self.netlist_file)
-        self.log_file = netlist_radic + '.log'
-
-        # Cleanup everything
-        if self.retcode == 0:
-            # simulation successful
-            logger.info("Simulation Successful. Time elapsed: %s" % sim_time)
-            if self.verbose:
-                print(time.asctime() + ": Simulation Successful. Time elapsed %s:%s" % (sim_time, END_LINE_TERM))
-
-            self.raw_file = netlist_radic + '.raw'
-
-            if os.path.exists(self.raw_file) and os.path.exists(self.log_file):
-                if self.callback:
-                    if self.verbose:
-                        print("Calling the callback function")
-                    try:
-                        self.callback(self.raw_file, self.log_file)
-                    except Exception as err:
-                        error = traceback.format_tb(err)
-                        logger.error(error)
-                else:
-                    if self.verbose:
-                        print('No Callback')
-            else:
-                logger.error("Simulation Raw file or Log file were not found")
-        else:
-            # simulation failed
-
-            logger.warning(time.asctime() + ": Simulation Failed. Time elapsed %s:%s" % (sim_time, END_LINE_TERM))
-            if os.path.exists(self.log_file):
-                old_log_file = self.log_file
-                self.log_file = netlist_radic + '.fail'
-                os.rename(old_log_file, self.log_file)
-
-    def wait_results(self) -> Tuple[str, str]:
-        """
-        Waits for the completion of the task and returns a tuple with the raw and log files.
-        :returns: Tupple with the path to the raw file and the path to the log file
-        :rtype: tuple(str, str)
-        """
-        while self.is_alive() or self.retcode == -1:
-            sleep(0.1)
-        if self.retcode == 0:  # All finished OK
-            return self.raw_file, self.log_file
-        else:
-            return '', ''
-
-
-
[docs]class SimCommander(SpiceEditor): - """ - The SimCommander class implements all the methods required for launching batches of LTSpice simulations. - It takes a parameter the path to the LTSpice .asc file to be simulated, or directly the .net file. - If an .asc file is given, the class will try to generate the respective .net file by calling LTspice with - the --netlist option - - :param circuit_file: Path to the circuit to simulate. It can be either a .asc or a .net file - :type circuit_file: str - :param parallel_sims: Defines the number of parallel simulations that can be executed at the same time. Ideally this - number should be aligned to the number of CPUs (processor cores) available on the machine. - :type parallel_sims: int, optional - :param timeout: Timeout parameter as specified on the os subprocess.run() function - :type timeout: float, optional - :param verbose: If True, it enables a richer printout of the program execution. - :type verbose: bool, optional - :param encoding: Forcing the encoding to be used on the circuit netlile read. Defaults to 'autodetect' which will - call a function that tries to detect the encoding automatically. This however is not 100% fool - proof. - :type encoding: str, optional - :param simulator: Forcing a given simulator executable. - :type simulator: str or Simulator, optional - """ - - def __init__(self, circuit_file: str, parallel_sims: int = 4, timeout=None, verbose=True, encoding='autodetect', - simulator=None): - """ - Class Constructor. It serves to start batches of simulations. - See Class documentation for more information. - """ - - self.verbose = verbose - self.timeout = timeout - - self.file_path = os.path.dirname(circuit_file) - if self.file_path == '': - self.file_path = os.path.abspath(os.curdir) - self.file_name, file_ext = os.path.splitext(os.path.basename(circuit_file)) - self.circuit_radic = os.path.join(self.file_path, self.file_name) - - self.parallel_sims = parallel_sims - self.threads = [] - - # master_log_filename = self.circuit_radic + '.masterlog' TODO: create the JSON or YAML file - self.logger = logging.getLogger("SimCommander") - self.logger.setLevel(logging.INFO) - # TODO redirect this logger to a file. - - self.runno = 0 # number of total runs - self.failSim = 0 # number of failed simulations - self.okSim = 0 # number of succesfull completed simulations - # self.failParam = [] # collects for later user investigation of failed parameter sets - - # Gets a simulator. - if simulator is None: - self.simulator = Simulator.get_default_simulator() - elif isinstance(simulator, Simulator): - self.simulator = simulator - elif isinstance(simulator, (str, pathlib.Path)): - self.simulator = Simulator.create_from(simulator) - else: - raise TypeError("Invalid simulator type. Either use a string with the ") - - if file_ext == '.asc': - netlist_file = self.circuit_radic + '.net' - if self.verbose: - print("Creating Netlist") - retcode = self.simulator.create_netlist(circuit_file) - if retcode == 0 and os.path.exists(netlist_file): - if self.verbose: - print("The Netlist was successfully created") - else: - if self.verbose: - print("Unable to create the Netlist from %s" % circuit_file) - netlist_file = None - elif os.path.exists(circuit_file): - netlist_file = circuit_file - else: - netlist_file = None - if self.verbose: - print("Unable to find the Netlist: %s" % circuit_file) - - super(SimCommander, self).__init__(netlist_file, encoding=encoding) - self.reset_netlist() - if len(self.netlist) == 0: - self.logger.error("Unable to create Netlist") - - def __del__(self): - """Class Destructor : Closes Everything""" - self.logger.debug("Waiting for all spawned threads to finish.") - self.wait_completion() # TODO: Kill all pending simulations - self.logger.debug("Exiting SimCommander") - -
[docs] def setLTspiceRunCommand(self, spice_tool: Union[str, Simulator]) -> None: - """ - Manually setting the LTSpice run command - - :param spice_tool: String containing the path to the spice tool to be used, or alternatively the Simulator - object. - :type spice_tool: str or Simulator - :return: Nothing - :rtype: None - """ - if isinstance(spice_tool, str): - self.simulator = Simulator.create_from(spice_tool) - elif isinstance(spice_tool, Simulator): - self.simulator = spice_tool - else: - raise TypeError("Expecting str or Simulator objects")
- -
[docs] def add_LTspiceRunCmdLineSwitches(self, *args) -> None: - """ - Used to add an extra command line argument such as -I<path> to add symbol search path or -FastAccess - to convert the raw file into Fast Access. - The arguments is a list of strings as is defined in the LTSpice command line documentation. - - :param args: list of strings - A list of command line switches such as "-ascii" for generating a raw file in text format or "-alt" for - setting the solver to alternate. See Command Line Switches information on LTSpice help file. - :type args: list[str] - :returns: Nothing - """ - self.simulator.add_command_line_switche(*args)
- -
[docs] def run(self, run_filename: str = None, wait_resource: bool = True, - callback: Callable[[str, str], Any] = None, timeout: float = 600) -> RunTask: - """ - Executes a simulation run with the conditions set by the user. - Conditions are set by the set_parameter, set_component_value or add_instruction functions. - - :param run_filename: - The name of the netlist can be optionally overridden if the user wants to have a better control of how the - simulations files are generated. - :type run_filename: str, optional - :param wait_resource: - Setting this parameter to False will force the simulation to start immediately, irrespective of the number - of simulations already active. - By default the SimCommander class uses only four processors. This number can be overridden by setting - the parameter ´parallel_sims´ to a different number. - If there are more than ´parallel_sims´ simulations being done, the new one will be placed on hold till one - of the other simulations are finished. - :type wait_resource: bool, optional - :param callback: - The user can optionally give a callback function for when the simulation finishes so that processing can - be done immediately. - :type: callback: function(raw_file, log_file), optional - :param timeout: Timeout to be used in waiting for resources. Default time is 600 seconds, i.e. 10 minutes. - :type timeout: float, optional - - :returns: The task object of type RunTask - """ - # decide sim required - if self.netlist is not None: - # update number of simulation - self.runno += 1 # Using internal simulation number in case a run_id is not supplied - - # Write the new settings - if run_filename is None: - run_netlist_file = "%s_%i.net" % (self.circuit_radic, self.runno) - else: - run_netlist_file = run_filename - - self.write_netlist(run_netlist_file) - t0 = time.perf_counter() # Store the time for timeout calculation - while time.perf_counter() - t0 < timeout: - self.updated_stats() # purge ended tasks - - if (wait_resource is False) or (len(self.threads) < self.parallel_sims): - t = RunTask(self.simulator, self.runno, run_netlist_file, callback, - timeout=self.timeout, verbose=self.verbose) - self.threads.append(t) - t.start() - sleep(0.01) # Give slack for the thread to start - return t # Returns the task number - sleep(0.1) # Give Time for other simulations to end - else: - self.logger.error("Timeout waiting for resources for simulation %d" % self.runno) - if self.verbose: - print("Timeout on launching simulation %d." % self.runno) - - else: - # no simulation required - raise UserWarning('skipping simulation ' + str(self.runno))
- -
[docs] def updated_stats(self): - """ - This function updates the OK/Fail statistics and releases finished RunTask objects from memory. - - :returns: Nothing - """ - i = 0 - while i < len(self.threads): - if self.threads[i].is_alive(): - i += 1 - else: - if self.threads[i].retcode == 0: - self.okSim += 1 - else: - # simulation failed - self.failSim += 1 - del self.threads[i]
- -
[docs] @staticmethod - def kill_all_ltspice(): - """Function to terminate LTSpice in windows""" - simulator = Simulator.get_default_simulator() - simulator.kill_all()
- -
[docs] def wait_completion(self, timeout=None, abort_all_on_timeout=False) -> bool: - """ - This function will wait for the execution of all scheduled simulations to complete. - - :param timeout: Cancels the wait after the number of seconds specified by the timeout. - This timeout is reset everytime that a simulation is completed. The difference between this timeout and the - one defined in the SimCommander instance, is that the later is implemented by the subprocess class, and the - this timeout just cancels the wait. - :type timeout: int - :param abort_all_on_timeout: attempts to stop all LTSpice processes if timeout is expired. - :type abort_all_on_timeout: bool - :returns: True if all simulations were executed successfully - :rtype: bool - """ - self.updated_stats() - timeout_counter = 0 - sim_counters = (self.okSim, self.failSim) - - while len(self.threads) > 0: - sleep(1) - self.updated_stats() - if timeout is not None: - if sim_counters == (self.okSim, self.failSim): - timeout_counter += 1 - print(timeout_counter, "timeout counter") - else: - timeout_counter = 0 - - if timeout_counter > timeout: - if abort_all_on_timeout: - self.kill_all_ltspice() - return False - - return self.failSim == 0
- - -if __name__ == "__main__": - # get script absolute path - meAbsPath = os.path.dirname(os.path.realpath(__file__)) - meAbsPath, _ = os.path.split(meAbsPath) - # select spice model - LTC = SimCommander(meAbsPath + "\\test_files\\testfile.asc") - # set default arguments - LTC.set_parameters(res=0.001, cap=100e-6) - # define simulation - LTC.add_instructions( - "; Simulation settings", - # [".STEP PARAM Rmotor LIST 21 28"], - ".TRAN 3m", - # ".step param run 1 2 1" - ) - # do parameter sweep - for res in range(5): - # LTC.runs_to_do = range(2) - LTC.set_parameters(ANA=res) - raw, log = LTC.run() - print("Raw file '%s' | Log File '%s'" % (raw, log)) - # Sim Statistics - print('Successful/Total Simulations: ' + str(LTC.okSim) + '/' + str(LTC.runno)) - - - def callback_function(raw_file, log_file): - print("Handling the simulation data of %s, log file %s" % (raw_file, log_file)) - - - LTC = SimCommander(meAbsPath + "\\test_files\\testfile.asc", parallel_sims=1) - tstart = 0 - for tstop in (2, 5, 8, 10): - tduration = tstop - tstart - LTC.add_instruction(".tran {}".format(tduration), ) - if tstart != 0: - LTC.add_instruction(".loadbias {}".format(bias_file)) - # Put here your parameter modifications - # LTC.set_parameters(param1=1, param2=2, param3=3) - bias_file = "sim_loadbias_%d.txt" % tstop - LTC.add_instruction(".savebias {} internal time={}".format(bias_file, tduration)) - tstart = tstop - LTC.run(callback=callback_function) - - LTC.reset_netlist() - LTC.add_instruction('.ac dec 40 1m 1G') - LTC.set_component_value('V1', 'AC 1 0') - LTC.run(callback=callback_function) - LTC.wait_completion() -
- -
-
-
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/doc_build/html/_modules/index.html b/doc_build/html/_modules/index.html deleted file mode 100644 index c28664d..0000000 --- a/doc_build/html/_modules/index.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - Overview: module code — PyLTSpice 3.0 documentation - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doc_build/html/_sources/index.rst.txt b/doc_build/html/_sources/index.rst.txt deleted file mode 100644 index 430b68d..0000000 --- a/doc_build/html/_sources/index.rst.txt +++ /dev/null @@ -1,29 +0,0 @@ -.. PyLTSpice documentation master file, created by - sphinx-quickstart on Thu Jul 23 10:25:30 2020. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to PyLTSpice's documentation! -===================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - -<<<<<<< HEAD - modules/modules - classes/classes - utilities/utilities -======= - utilities/utilities - modules/modules - classes/classes ->>>>>>> master - varia/varia - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc_build/html/_static/basic.css b/doc_build/html/_static/basic.css deleted file mode 100644 index 288578b..0000000 --- a/doc_build/html/_static/basic.css +++ /dev/null @@ -1,855 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 20em; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 450px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a.brackets:before, -span.brackets > a:before{ - content: "["; -} - -a.brackets:after, -span.brackets > a:after { - content: "]"; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -dl.footnote > dt, -dl.citation > dt { - float: left; - margin-right: 0.5em; -} - -dl.footnote > dd, -dl.citation > dd { - margin-bottom: 0em; -} - -dl.footnote > dd:after, -dl.citation > dd:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dt:after { - content: ":"; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0.5em; - content: ":"; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -code.descclassname { - background-color: transparent; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/doc_build/html/_static/doctools.js b/doc_build/html/_static/doctools.js deleted file mode 100644 index daccd20..0000000 --- a/doc_build/html/_static/doctools.js +++ /dev/null @@ -1,315 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for all documentation. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/** - * select a different prefix for underscore - */ -$u = _.noConflict(); - -/** - * make the code below compatible with browsers without - * an installed firebug like debugger -if (!window.console || !console.firebug) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", - "profile", "profileEnd"]; - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {}; -} - */ - -/** - * small helper function to urldecode strings - */ -jQuery.urldecode = function(x) { - return decodeURIComponent(x).replace(/\+/g, ' '); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { - this.initOnKeyListeners(); - } - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated === 'undefined') - return string; - return (typeof translated === 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated === 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - if (!body.length) { - body = $('body'); - } - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) === 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this === '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - }, - - initOnKeyListeners: function() { - $(document).keydown(function(event) { - var activeElementType = document.activeElement.tagName; - // don't navigate when in search box or textarea - if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' - && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) { - switch (event.keyCode) { - case 37: // left - var prevHref = $('link[rel="prev"]').prop('href'); - if (prevHref) { - window.location.href = prevHref; - return false; - } - case 39: // right - var nextHref = $('link[rel="next"]').prop('href'); - if (nextHref) { - window.location.href = nextHref; - return false; - } - } - } - }); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/doc_build/html/_static/documentation_options.js b/doc_build/html/_static/documentation_options.js deleted file mode 100644 index 156e75e..0000000 --- a/doc_build/html/_static/documentation_options.js +++ /dev/null @@ -1,12 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '3.0', - LANGUAGE: 'None', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false -}; \ No newline at end of file diff --git a/doc_build/html/_static/file.png b/doc_build/html/_static/file.png deleted file mode 100644 index a858a410e4faa62ce324d814e4b816fff83a6fb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( diff --git a/doc_build/html/_static/language_data.js b/doc_build/html/_static/language_data.js deleted file mode 100644 index d2b4ee9..0000000 --- a/doc_build/html/_static/language_data.js +++ /dev/null @@ -1,297 +0,0 @@ -/* - * language_data.js - * ~~~~~~~~~~~~~~~~ - * - * This script contains the language-specific data used by searchtools.js, - * namely the list of stopwords, stemmer, scorer and splitter. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"]; - - -/* Non-minified version JS is _stemmer.js if file is provided */ -/** - * Porter Stemmer - */ -var Stemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - - - - - -var splitChars = (function() { - var result = {}; - var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648, - 1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702, - 2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971, - 2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345, - 3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761, - 3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823, - 4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125, - 8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695, - 11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587, - 43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141]; - var i, j, start, end; - for (i = 0; i < singles.length; i++) { - result[singles[i]] = true; - } - var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709], - [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161], - [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568], - [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807], - [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047], - [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383], - [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450], - [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547], - [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673], - [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820], - [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946], - [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023], - [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173], - [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332], - [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481], - [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718], - [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791], - [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095], - [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205], - [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687], - [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968], - [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869], - [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102], - [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271], - [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592], - [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822], - [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167], - [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959], - [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143], - [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318], - [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483], - [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101], - [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567], - [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292], - [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444], - [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783], - [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311], - [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511], - [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774], - [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071], - [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263], - [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519], - [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647], - [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967], - [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295], - [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274], - [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007], - [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381], - [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]]; - for (i = 0; i < ranges.length; i++) { - start = ranges[i][0]; - end = ranges[i][1]; - for (j = start; j <= end; j++) { - result[j] = true; - } - } - return result; -})(); - -function splitQuery(query) { - var result = []; - var start = -1; - for (var i = 0; i < query.length; i++) { - if (splitChars[query.charCodeAt(i)]) { - if (start !== -1) { - result.push(query.slice(start, i)); - start = -1; - } - } else if (start === -1) { - start = i; - } - } - if (start !== -1) { - result.push(query.slice(start)); - } - return result; -} - - diff --git a/doc_build/html/_static/minus.png b/doc_build/html/_static/minus.png deleted file mode 100644 index d96755fdaf8bb2214971e0db9c1fd3077d7c419d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK diff --git a/doc_build/html/_static/plus.png b/doc_build/html/_static/plus.png deleted file mode 100644 index 7107cec93a979b9a5f64843235a16651d563ce2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz diff --git a/doc_build/html/_static/pygments.css b/doc_build/html/_static/pygments.css deleted file mode 100644 index b0ec841..0000000 --- a/doc_build/html/_static/pygments.css +++ /dev/null @@ -1,77 +0,0 @@ -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #8f5902; font-style: italic } /* Comment */ -.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ -.highlight .g { color: #000000 } /* Generic */ -.highlight .k { color: #204a87; font-weight: bold } /* Keyword */ -.highlight .l { color: #000000 } /* Literal */ -.highlight .n { color: #000000 } /* Name */ -.highlight .o { color: #ce5c00; font-weight: bold } /* Operator */ -.highlight .x { color: #000000 } /* Other */ -.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ -.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ -.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #a40000 } /* Generic.Deleted */ -.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #ef2929 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #000000; font-style: italic } /* Generic.Output */ -.highlight .gp { color: #8f5902 } /* Generic.Prompt */ -.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ -.highlight .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ -.highlight .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #204a87; font-weight: bold } /* Keyword.Type */ -.highlight .ld { color: #000000 } /* Literal.Date */ -.highlight .m { color: #0000cf; font-weight: bold } /* Literal.Number */ -.highlight .s { color: #4e9a06 } /* Literal.String */ -.highlight .na { color: #c4a000 } /* Name.Attribute */ -.highlight .nb { color: #204a87 } /* Name.Builtin */ -.highlight .nc { color: #000000 } /* Name.Class */ -.highlight .no { color: #000000 } /* Name.Constant */ -.highlight .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ -.highlight .ni { color: #ce5c00 } /* Name.Entity */ -.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #000000 } /* Name.Function */ -.highlight .nl { color: #f57900 } /* Name.Label */ -.highlight .nn { color: #000000 } /* Name.Namespace */ -.highlight .nx { color: #000000 } /* Name.Other */ -.highlight .py { color: #000000 } /* Name.Property */ -.highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #000000 } /* Name.Variable */ -.highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ -.highlight .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */ -.highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ -.highlight .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ -.highlight .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ -.highlight .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ -.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ -.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ -.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ -.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ -.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ -.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ -.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ -.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ -.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ -.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ -.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ -.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ -.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #000000 } /* Name.Function.Magic */ -.highlight .vc { color: #000000 } /* Name.Variable.Class */ -.highlight .vg { color: #000000 } /* Name.Variable.Global */ -.highlight .vi { color: #000000 } /* Name.Variable.Instance */ -.highlight .vm { color: #000000 } /* Name.Variable.Magic */ -.highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/doc_build/html/_static/searchtools.js b/doc_build/html/_static/searchtools.js deleted file mode 100644 index 970d0d9..0000000 --- a/doc_build/html/_static/searchtools.js +++ /dev/null @@ -1,514 +0,0 @@ -/* - * searchtools.js - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for the full-text search. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -if (!Scorer) { - /** - * Simple result scoring code. - */ - var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [filename, title, anchor, descr, score] - // and returns the new score. - /* - score: function(result) { - return result[4]; - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: {0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5}, // used to be unimportantResults - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - partialTitle: 7, - // query found in terms - term: 5, - partialTerm: 2 - }; -} - -if (!splitQuery) { - function splitQuery(query) { - return query.split(/\s+/); - } -} - -/** - * Search Module - */ -var Search = { - - _index : null, - _queued_query : null, - _pulse_status : -1, - - htmlToText : function(htmlString) { - var htmlElement = document.createElement('span'); - htmlElement.innerHTML = htmlString; - $(htmlElement).find('.headerlink').remove(); - docContent = $(htmlElement).find('[role=main]')[0]; - if(docContent === undefined) { - console.warn("Content block not found. Sphinx search tries to obtain it " + - "via '[role=main]'. Could you check your theme or template."); - return ""; - } - return docContent.textContent || docContent.innerText; - }, - - init : function() { - var params = $.getQueryParameters(); - if (params.q) { - var query = params.q[0]; - $('input[name="q"]')[0].value = query; - this.performSearch(query); - } - }, - - loadIndex : function(url) { - $.ajax({type: "GET", url: url, data: null, - dataType: "script", cache: true, - complete: function(jqxhr, textstatus) { - if (textstatus != "success") { - document.getElementById("searchindexloader").src = url; - } - }}); - }, - - setIndex : function(index) { - var q; - this._index = index; - if ((q = this._queued_query) !== null) { - this._queued_query = null; - Search.query(q); - } - }, - - hasIndex : function() { - return this._index !== null; - }, - - deferQuery : function(query) { - this._queued_query = query; - }, - - stopPulse : function() { - this._pulse_status = 0; - }, - - startPulse : function() { - if (this._pulse_status >= 0) - return; - function pulse() { - var i; - Search._pulse_status = (Search._pulse_status + 1) % 4; - var dotString = ''; - for (i = 0; i < Search._pulse_status; i++) - dotString += '.'; - Search.dots.text(dotString); - if (Search._pulse_status > -1) - window.setTimeout(pulse, 500); - } - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch : function(query) { - // create the required interface elements - this.out = $('#search-results'); - this.title = $('

' + _('Searching') + '

').appendTo(this.out); - this.dots = $('').appendTo(this.title); - this.status = $('

 

').appendTo(this.out); - this.output = $('