├── .github └── workflows │ └── unittest.yml ├── .gitignore ├── .readthedocs.yaml ├── LICENSE ├── Makefile ├── PyLTSpice ├── Histogram.py ├── LTSteps.py ├── __init__.py ├── editor │ ├── __init__.py │ ├── asc_editor.py │ └── spice_editor.py ├── log │ ├── __init__.py │ ├── logfile_data.py │ ├── ltsteps.py │ └── semi_dev_op_reader.py ├── raw │ ├── __init__.py │ ├── raw_classes.py │ ├── raw_read.py │ └── raw_write.py ├── rawplot.py ├── run_server.py ├── sim │ ├── __init__.py │ ├── ltspice_simulator.py │ ├── process_callback.py │ ├── sim_batch.py │ ├── sim_runner.py │ ├── sim_stepping.py │ └── tookit │ │ ├── montecarlo.py │ │ └── worst_case.py └── utils │ ├── detect_encoding.py │ └── sweep_iterators.py ├── README.md ├── doc ├── classes │ ├── asc_editor.rst │ ├── editor_classes.rst │ ├── logreader.rst │ ├── raw_classes.rst │ ├── raw_read.rst │ ├── raw_write.rst │ ├── sim_batch.rst │ ├── sim_runner.rst │ ├── sim_simulator.rst │ ├── simulation_classes.rst │ ├── spice_circuit.rst │ ├── spice_editor.rst │ ├── trace.rst │ └── write_trace.rst ├── conf.py ├── index.rst ├── media │ └── Singleton approach.drawio ├── modules │ ├── SemiOpLogReader.rst │ ├── modules.rst │ ├── read_netlist.rst │ ├── read_rawfiles.rst │ ├── read_steps.rst │ ├── run_simulations.rst │ ├── sallenkey.png │ ├── sallenkey_mc.png │ ├── sallenkey_wc.png │ ├── sim_analysis.rst │ └── write_rawfiles.rst ├── requirements.txt ├── utilities │ ├── Histogram.rst │ ├── LTSteps.rst │ └── utilities.rst └── varia │ ├── LTSpice.rst │ ├── raw_file.rst │ └── varia.rst ├── examples ├── ltsteps_example.py ├── raw_plotting.py ├── raw_read_example.py ├── raw_write_example.py ├── run_montecarlo.py ├── run_worst_case.py ├── sim_runner_asc_example.py ├── sim_runner_callback_example.py ├── sim_runner_callback_process_example.py ├── sim_runner_example.py ├── sim_stepper_example.py ├── spice_editor_example.py └── testfiles │ ├── AC - STEP.asc │ ├── AC - STEP.log │ ├── AC - STEP.op.raw │ ├── AC - STEP.raw │ ├── AC - STEP_1.log │ ├── AC - STEP_1.raw │ ├── AC.asc │ ├── AC.log │ ├── AC.op.raw │ ├── AC.raw │ ├── AC_1.log │ ├── AC_1.raw │ ├── Batch_Test.asc │ ├── Batch_Test_1.log │ ├── Batch_Test_1.raw │ ├── Batch_Test_AD712_15.log │ ├── Batch_Test_AD712_15.raw │ ├── Batch_Test_AD820_15.log │ ├── Batch_Test_AD820_15.raw │ ├── DC op point - STEP.asc │ ├── DC op point - STEP.log │ ├── DC op point - STEP.raw │ ├── DC op point - STEP_1.log │ ├── DC op point - STEP_1.raw │ ├── DC op point.asc │ ├── DC op point_1.log │ ├── DC op point_1.raw │ ├── DC sweep.asc │ ├── DC sweep.raw │ ├── Noise.asc │ ├── Noise.log │ ├── Noise.op.raw │ ├── Noise.raw │ ├── Noise_updated.net │ ├── PI_Filter.raw │ ├── PI_Filter_resampled.raw │ ├── TRAN - STEP.asc │ ├── TRAN - STEP.log │ ├── TRAN - STEP.raw │ ├── TRAN - STEP_1.log │ ├── TRAN - STEP_1.raw │ ├── TRAN.asc │ ├── TRAN.log │ ├── TRAN.op.raw │ ├── TRAN.raw │ ├── TRAN_1.log │ ├── TRAN_1.raw │ ├── diode.asy │ ├── res.asy │ ├── sallenkey.asc │ ├── sub_circuit.asy │ ├── temp │ └── readme.txt │ ├── testfile.asc │ ├── testfile.log │ ├── testfile.net │ ├── testfile.raw │ ├── testfile_ngspice.net │ └── voltage.asy ├── make.bat ├── pyproject.toml └── unittests ├── golden ├── test_components_output.asc ├── test_components_output_1.asc ├── test_instructions_output.asc ├── test_instructions_output_1.asc └── test_parameter_output.asc ├── sweep_iterators_unittest.py ├── test_asc_editor.py └── test_pyltspice.py /.github/workflows/unittest.yml: -------------------------------------------------------------------------------- 1 | # ************************************************************************ 2 | # @author: Andreas Kaeberlein 3 | # @copyright: Copyright 2021 4 | # @credits: AKAE 5 | # 6 | # @license: GPLv3 7 | # @maintainer: Nuno Brum 8 | # @email: me@nunobrum.com 9 | # 10 | # @file: unittest.yml 11 | # @date: 2023-08-09 12 | # 13 | # @brief: runs unit test 14 | # 15 | # ************************************************************************ 16 | 17 | 18 | name: Unittest 19 | 20 | on: [push] 21 | 22 | jobs: 23 | Unittest: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | - uses: actions/setup-python@v3 28 | with: 29 | python-version: '3.8' # Version range or exact version of a Python version to use, using semvers version range syntax. 30 | architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install numpy 35 | pip install spicelib 36 | - name: Test sweep_iterators.py 37 | run: | 38 | python ./unittests/sweep_iterators_unittest.py 39 | - name: Test LTSpice_RawRead 40 | run: | 41 | python ./unittests/test_pyltspice.py 42 | - name: Test Asc_Editor 43 | run: | 44 | python ./unittests/test_asc_editor.py 45 | 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .idea/ 3 | __pycache__/ 4 | PyLTSpice.egg-info 5 | venv 6 | .venv 7 | .python-version 8 | *.bak 9 | *.log 10 | *.raw 11 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.12" 12 | 13 | # Build documentation in the docs/ directory with Sphinx 14 | sphinx: 15 | configuration: doc/conf.py 16 | 17 | # Optionally build your docs in additional formats such as PDF 18 | formats: 19 | - pdf 20 | 21 | # Optionally set the version of Python and requirements required to build your docs 22 | python: 23 | install: 24 | - requirements: doc/requirements.txt 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = doc 9 | BUILDDIR = doc_build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /PyLTSpice/Histogram.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: Histogram.py 13 | # Purpose: Make an histogram plot based on the results of an LTSpice log file 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Created: 17-01-2017 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | print("Histogram.py was moved into the spicelib package.") -------------------------------------------------------------------------------- /PyLTSpice/LTSteps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: LTSteps.py 13 | # Purpose: Process LTSpice output files and align data for usage in a spread- 14 | # sheet tool such as Excel, or Calc. 15 | # 16 | # Author: Nuno Brum (nuno.brum@gmail.com) 17 | # 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | print("LTSteps.py was moved into the spicelib package.") -------------------------------------------------------------------------------- /PyLTSpice/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Convenience direct imports 4 | from spicelib.raw.raw_read import RawRead, SpiceReadException 5 | from spicelib.raw.raw_write import RawWrite, Trace 6 | from spicelib.editor.spice_editor import SpiceEditor, SpiceCircuit 7 | from spicelib.editor.asc_editor import AscEditor 8 | from PyLTSpice.sim.sim_runner import SimRunner 9 | from spicelib.simulators.ltspice_simulator import LTspice 10 | from PyLTSpice.sim.sim_batch import SimCommander 11 | from spicelib.log.ltsteps import LTSpiceLogReader 12 | 13 | 14 | def all_loggers(): 15 | """ 16 | Returns all the name strings used as logger identifiers. 17 | 18 | :return: A List of strings which contains all the logger's names used in this library. 19 | :rtype: list[str] 20 | """ 21 | return [ 22 | "spicelib.RunTask", 23 | "spicelib.SimClient", 24 | "spicelib.SimServer", 25 | "spicelib.ServerSimRunner", 26 | "spicelib.LTSteps", 27 | "spicelib.RawRead", 28 | "spicelib.LTSpiceSimulator", 29 | "spicelib.SimBatch", 30 | "spicelib.SimRunner", 31 | "spicelib.SimStepper", 32 | "spicelib.SpiceEditor", 33 | "spicelib.SimBatch", 34 | "spicelib.AscEditor", 35 | "spicelib.LTSpiceSimulator", 36 | ] 37 | 38 | 39 | def set_log_level(level): 40 | """ 41 | Sets the logging level for all loggers used in the library. 42 | 43 | :param level: The logging level to be used, eg. logging.ERROR, logging.DEBUG, etc. 44 | :type level: int 45 | """ 46 | import logging 47 | for logger in all_loggers(): 48 | logging.getLogger(logger).setLevel(level) 49 | 50 | 51 | def add_log_handler(handler): 52 | """ 53 | Sets the logging handler for all loggers used in the library. 54 | 55 | :param handler: The logging handler to be used, eg. logging.NullHandler 56 | :type handler: Handler 57 | """ 58 | import logging 59 | for logger in all_loggers(): 60 | logging.getLogger(logger).addHandler(handler) 61 | -------------------------------------------------------------------------------- /PyLTSpice/editor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/PyLTSpice/editor/__init__.py -------------------------------------------------------------------------------- /PyLTSpice/editor/asc_editor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | # ------------------------------------------------------------------------------- 4 | # ____ _ _____ ____ _ 5 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 6 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 7 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 8 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 9 | # |___/ |_| 10 | # 11 | # Name: asc_editor.py 12 | # Purpose: Class made to update directly the ltspice ASC files 13 | # 14 | # Author: Nuno Brum (nuno.brum@gmail.com) 15 | # 16 | # Licence: refer to the LICENSE file 17 | # ------------------------------------------------------------------------------- 18 | import logging 19 | 20 | _logger = logging.getLogger("spicelib.AscEditor") 21 | _logger.info("This is maintained for backward compatibility. Use spicelib.editor.asc_editor instead") 22 | 23 | from spicelib.editor.asc_editor import * 24 | -------------------------------------------------------------------------------- /PyLTSpice/editor/spice_editor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | # ------------------------------------------------------------------------------- 4 | # ____ _ _____ ____ _ 5 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 6 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 7 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 8 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 9 | # |___/ |_| 10 | # 11 | # Name: spice_editor.py 12 | # Purpose: Class made to update Generic Spice netlists 13 | # 14 | # Author: Nuno Brum (nuno.brum@gmail.com) 15 | # 16 | # Licence: refer to the LICENSE file 17 | # ------------------------------------------------------------------------------- 18 | import logging 19 | from pathlib import Path 20 | from typing import Union, Callable, Any 21 | 22 | _logger = logging.getLogger("spicelib.SpiceEditor") 23 | _logger.info("This is maintained for compatibility issues. Use spicelib.editor.spice_editor instead") 24 | from spicelib.editor.spice_editor import SpiceEditor as SpiceEditorBase, SpiceCircuit 25 | from spicelib.simulators.ltspice_simulator import LTspice 26 | 27 | 28 | class SpiceEditor(SpiceEditorBase): 29 | 30 | def __init__(self, netlist_file: Union[str, Path], encoding='autodetect', create_blank=False): 31 | netlist_file = Path(netlist_file) 32 | if netlist_file.suffix == ".asc": 33 | LTspice.create_netlist(netlist_file) 34 | netlist_file = netlist_file.with_suffix(".net") 35 | super().__init__(netlist_file, encoding, create_blank) 36 | 37 | def run(self, wait_resource: bool = True, callback: Callable[[str, str], Any] = None, timeout: float = 600, 38 | run_filename: str = None, simulator=None): 39 | if simulator is None: 40 | simulator = LTspice 41 | return super().run(wait_resource, callback, timeout, run_filename, simulator) 42 | 43 | -------------------------------------------------------------------------------- /PyLTSpice/log/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/PyLTSpice/log/__init__.py -------------------------------------------------------------------------------- /PyLTSpice/log/logfile_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # Name: logfile_data.py 6 | # Purpose: Store data related to log files. This is a superclass of LTSpiceLogReader 7 | # 8 | # Author: Nuno Brum (nuno.brum@gmail.com) 9 | # 10 | # Licence: refer to the LICENSE file 11 | # ------------------------------------------------------------------------------- 12 | 13 | import logging 14 | 15 | _logger = logging.getLogger("spicelib.LTSteps") 16 | _logger.info("This module is deprecated. Use spicelib.log.logfile_data instead") 17 | from spicelib.log.logfile_data import LogfileData, LTComplex 18 | -------------------------------------------------------------------------------- /PyLTSpice/log/ltsteps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # Name: ltsteps.py 6 | # Purpose: Process LTSpice output files and align data for usage in a spread- 7 | # sheet tool such as Excel, or Calc. 8 | # 9 | # Author: Nuno Brum (nuno.brum@gmail.com) 10 | # 11 | # Licence: refer to the LICENSE file 12 | # ------------------------------------------------------------------------------- 13 | import logging 14 | 15 | _logger = logging.getLogger("spicelib.LTSteps") 16 | _logger.info("This module is maintained for backward compatibility. Use spicelib.log.ltsteps instead") 17 | 18 | from spicelib.log.ltsteps import reformat_LTSpice_export, LTSpiceExport, LTSpiceLogReader 19 | -------------------------------------------------------------------------------- /PyLTSpice/log/semi_dev_op_reader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: SemiDevOpReader.py 13 | # Purpose: Read Semiconductor Device Operating Points from a log file 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Created: 19-09-2021 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | 21 | from spicelib.log.semi_dev_op_reader import opLogReader 22 | -------------------------------------------------------------------------------- /PyLTSpice/raw/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/PyLTSpice/raw/__init__.py -------------------------------------------------------------------------------- /PyLTSpice/raw/raw_classes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: raw_classes.py 13 | # Purpose: Implements helper classes that contain 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Created: 19-06-2022 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | """ 21 | Defines base classes for the RAW file data structures. 22 | """ 23 | from spicelib.raw.raw_classes import * 24 | 25 | -------------------------------------------------------------------------------- /PyLTSpice/raw/raw_read.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | # ------------------------------------------------------------------------------- 4 | # ____ _ _____ ____ _ 5 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 6 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 7 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 8 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 9 | # |___/ |_| 10 | # 11 | # Name: raw_read.py 12 | # Purpose: Process LTSpice output files and align data for usage in a spreadsheet 13 | # tool such as Excel, or Calc. 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Licence: refer to the LICENSE file 18 | # ------------------------------------------------------------------------------- 19 | 20 | """ 21 | This module reads data from an LTSpice RAW file. 22 | The main class object is the RawRead which is initialized with the filename of the RAW file to be processed. 23 | The object wil read the file and construct a structure of objects which can be used to access the data inside the 24 | RAW file. 25 | To understand why this is done so, in the next section follows a brief explanation of what is contained inside a RAW 26 | file. 27 | In case RAW file contains stepped data detected, i.e. when the .STEP command is used, then it will also try to open the 28 | simulation LOG file and read the stepping information. 29 | 30 | RAW File Structure 31 | ================== 32 | 33 | This section is written to help understand the why the structure of classes is defined as it is. You can gladly skip 34 | this section and get right down to business by seeing the examples section below. 35 | 36 | The RAW file starts with a text preamble that contains information about the names of the traces the order they 37 | appear on the binary part and some extra information. 38 | In the preamble, the lines are always started by one of the following identifiers: 39 | 40 | + Title: => Contains the path of the source .asc file used to make the simulation preceded by * 41 | 42 | + Date: => Date when the simulation started 43 | 44 | + Plotname: => Name of the simulation. The known Simulation Types are: 45 | * Operation Point 46 | * DC transfer characteristic 47 | * AC Analysis 48 | * Transient Analysis 49 | * Noise Spectral Density - (V/Hz½ or A/Hz½) 50 | * Transfer Function 51 | 52 | + Flags: => Flags that are used in this plot. The simulation can have any combination of these flags. 53 | * "real" -> The traces in the raw file contain real values. As for example on a TRAN simulation. 54 | * "complex" -> Traces in the raw file contain complex values. As for example on an AC simulation. 55 | * "forward" -> Tells whether the simulation has more than one point. DC transfer 56 | characteristic, AC Analysis, Transient Analysis or Noise Spectral Density have the forward flag. 57 | Operating Point and Transfer Function don't have this flag activated. 58 | * "log" -> The preferred plot view of this data is logarithmic. 59 | * "stepped" -> The simulation had .STEP primitives. 60 | * "FastAccess" -> Order of the data is changed to speed up access. See Binary section for details. 61 | 62 | + No. Variables: => number of variables contained in this dataset. See section below for details. 63 | 64 | + No. Points: => number of points per each variable in 65 | 66 | + Offset: => when the saving of data started 67 | 68 | + Command: => Name of the simulator executable generating this file. 69 | 70 | + Backannotation: => Backannotation alerts that occurred during simulation 71 | 72 | + Variables: => a list of variable, one per line as described below 73 | 74 | + Binary: => Start of the binary section. See section below for details. 75 | 76 | Variables List 77 | -------------- 78 | The variable list contains the list of measurements saved in the raw file. The order of the variables defines how they 79 | are stored in the binary section. The format is one variable per line, using the following format: 80 | 81 | 82 | 83 | Here is an example: 84 | 85 | .. code-block:: text 86 | 87 | 0 time time 88 | 1 V(n001) voltage 89 | 2 V(n004) voltage 90 | 3 V(n003) voltage 91 | 4 V(n006) voltage 92 | 5 V(adcc) voltage 93 | 6 V(n002) voltage 94 | 7 V(3v3_m) voltage 95 | 8 V(n005) voltage 96 | 9 V(n007) voltage 97 | 10 V(24v_dsp) voltage 98 | 11 I(C3) device_current 99 | 12 I(C2) device_current 100 | 13 I(C1) device_current 101 | 14 I(I1) device_current 102 | 15 I(R4) device_current 103 | 16 I(R3) device_current 104 | 17 I(V2) device_current 105 | 18 I(V1) device_current 106 | 19 Ix(u1:+) subckt_current 107 | 20 Ix(u1:-) subckt_current 108 | 109 | Binary Section 110 | -------------- 111 | The binary section of .RAW file is where the data is usually written, unless the user had explicitly specified an ASCII 112 | representation. In this case this section is replaced with a "Values" section. 113 | LTSpice stores data directly onto the disk during simulation, writing per each time or frequency step the list of 114 | values, as exemplified below for a .TRAN simulation. 115 | 116 | ... 117 | 118 | ... 119 | 120 | ... 121 | 122 | ... 123 | 124 | ... 125 | 126 | Depending on the type of simulation the type of data changes. 127 | On TRAN simulations the timestamp is always stored as 8 bytes float (double) and trace values as 4 bytes (single). 128 | On AC simulations the data is stored in complex format, which includes a real part and an imaginary part, each with 8 129 | bytes. 130 | The way we determine the size of the data is dividing the total block size by the number of points, then taking only 131 | the integer part. 132 | 133 | Fast Access 134 | ----------- 135 | 136 | Once a simulation is done, the user can ask LTSpice to optimize the data structure in such that variables are stored 137 | contiguously as illustrated below. 138 | 139 | ... 140 | 141 | ... 142 | 143 | ... 144 | 145 | ... 146 | 147 | ... 148 | 149 | ... 150 | 151 | This can speed up the data reading. Note that this transformation is not done automatically. Transforming data to Fast 152 | Access must be requested by the user. If the transformation is done, it is registered in the Flags: line in the 153 | header. RawReader supports both Normal and Fast Access formats 154 | 155 | Classes Defined 156 | =============== 157 | 158 | The .RAW file is read during the construction (constructor method) of an `RawRead` object. All traces on the RAW 159 | file are uploaded into memory. 160 | 161 | The RawRead class then has all the methods that allow the user to access the Axis and Trace Values. If there is 162 | any stepped data (.STEP primitives), the RawRead class will try to load the log information from the same 163 | directory as the raw file in order to obtain the STEP information. 164 | 165 | Follows an example of the RawRead class usage. Information on the RawRead methods can be found here. 166 | 167 | Examples 168 | ======== 169 | 170 | The example below demonstrates the usage of the RawRead class. It reads a .RAW file and uses the matplotlib 171 | library to plot the results of three traces in two subplots. :: 172 | 173 | import matplotlib.pyplot as plt # Imports the matplotlib library for plotting the results 174 | 175 | LTR = RawRead("some_random_file.raw") # Reads the RAW file contents from file 176 | 177 | print(LTR.get_trace_names()) # Prints the contents of the RAW file. The result is a list, and print formats it. 178 | print(LTR.get_raw_property()) # Prints all the properties found in the Header section. 179 | 180 | plt.figure() # Creates the canvas for plotting 181 | 182 | vin = LTR.get_trace('V(in)') # Gets the trace data. If Numpy is installed, then it comes in numpy array format. 183 | vout = LTR.get_trace('V(out)') # Gets the second trace. 184 | 185 | steps = LTR.get_steps() # Gets the step information. Returns a list of step numbers, ex: [0,1,2...]. If no steps 186 | # are present on the RAW file, returns only one step : [0] . 187 | 188 | fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True) # Creates the two subplots. One on top of the other. 189 | 190 | for ax in (ax1, ax2): # Crates a grid on all the plots. 191 | ax.grid(True) 192 | 193 | plt.xlim([0.9e-3, 1.2e-3]) # Optionally, limits the X axis to just a subrange. 194 | 195 | x = LTR.get_axis(0) # Retrieves the time vector that will be used as X axis. Uses STEP 0 196 | ax1.plot(x, vin.get_wave(0)) # On first plot plots the first STEP (=0) of Vin 197 | 198 | for step in steps: # On the second plot prints all the STEPS of the Vout 199 | x = LTR.get_axis(step) # Retrieves the time vector that will be used as X axis. 200 | ax2.plot(x, vout.get_wave(step)) 201 | 202 | plt.show() # Creates the matplotlib's interactive window with the plots. 203 | 204 | """ 205 | 206 | __author__ = "Nuno Canto Brum " 207 | __copyright__ = "Copyright 2022, Fribourg Switzerland" 208 | 209 | import logging 210 | _logger = logging.getLogger("spicelib.RawRead") 211 | _logger.info("This is maintained for backward compatibility. Use spicelib.raw.raw_read instead") 212 | 213 | from spicelib.raw.raw_read import RawRead 214 | # Backward compatibility naming 215 | LTSpiceRawRead = RawRead 216 | -------------------------------------------------------------------------------- /PyLTSpice/raw/raw_write.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: raw_write.py 13 | # Purpose: Create RAW Files 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Created: 16-10-2021 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | 21 | """ 22 | This module generates RAW Files from user data. 23 | It can be used to combine RAW files generated by different Simulation Runs 24 | """ 25 | from spicelib.raw.raw_write import Trace, RawWrite 26 | -------------------------------------------------------------------------------- /PyLTSpice/rawplot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: rawplot.py 13 | # Purpose: Draws a plot based on the results of an LTSpice raw file 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Created: 17-01-2017 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | print("rawplot.py was moved into the spicelib package.") -------------------------------------------------------------------------------- /PyLTSpice/run_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: run_server.py 13 | # Purpose: A Command Line Interface to run the LTSpice Server 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Created: 10-08-2023 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | print("run_server.py was moved into the spicelib package.") -------------------------------------------------------------------------------- /PyLTSpice/sim/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/PyLTSpice/sim/__init__.py -------------------------------------------------------------------------------- /PyLTSpice/sim/ltspice_simulator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: ltspice_simulator.py 13 | # Purpose: Represents a LTspice tool and it's command line options 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Created: 23-12-2016 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | import sys 21 | import os 22 | 23 | from pathlib import Path 24 | from typing import Union 25 | import logging 26 | _logger = logging.getLogger("spicelib.LTSpiceSimulator") 27 | _logger.info("This is maintained for backward compatibility. Use spicelib.sim.ltspice_simulator instead") 28 | from spicelib.sim.simulator import run_function 29 | from spicelib.simulators.ltspice_simulator import LTspice 30 | 31 | -------------------------------------------------------------------------------- /PyLTSpice/sim/process_callback.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: process_callback.py 13 | # Purpose: Being able to execute callback in a separate process 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Created: 23-04-2023 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | """ 21 | 22 | """ 23 | from spicelib.sim.process_callback import ProcessCallback 24 | 25 | -------------------------------------------------------------------------------- /PyLTSpice/sim/sim_batch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: sim_batch.py 13 | # Purpose: Tool used to launch LTSpice simulation in batch mode. Netlists can 14 | # be updated by user instructions 15 | # 16 | # Author: Nuno Brum (nuno.brum@gmail.com) 17 | # 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | """ 21 | ** This class is still maintained for backward compatibility. The user is invited to use the SpiceEditor and SimRunner 22 | classes instead. These give more flexibility in the command of simulations.** 23 | 24 | Allows launching LTSpice simulations from a Python Script, thus allowing to overcome the 3 dimensions STEP limitation on 25 | LTSpice, update resistor values, or component models. 26 | 27 | The code snipped below will simulate a circuit with two different diode models, set the simulation 28 | temperature to 80 degrees, and update the values of R1 and R2 to 3.3k. :: 29 | 30 | LTC = SimCommander("my_circuit.asc") 31 | LTC.set_parameters(temp=80) # Sets the simulation temperature to be 80 degrees 32 | LTC.set_component_value('R2', '3.3k') # Updates the resistor R2 value to be 3.3k 33 | for dmodel in ("BAT54", "BAT46WJ"): 34 | LTC.set_element_model("D1", model) # Sets the Diode D1 model 35 | for res_value in sweep(2.2, 2,4, 0.2): # Steps from 2.2 to 2.4 with 0.2 increments 36 | LTC.set_component_value('R1', res_value) # Updates the resistor R1 value to be 3.3k 37 | LTC.run() 38 | 39 | LTC.wait_completion() # Waits for the LTSpice simulations to complete 40 | 41 | print("Total Simulations: {}".format(LTC.runno)) 42 | print("Successful Simulations: {}".format(LTC.okSim)) 43 | print("Failed Simulations: {}".format(LTC.failSim)) 44 | 45 | The first line will create an python class instance that represents the LTSpice file or netlist that is to be 46 | simulated. This object implements methods that are used to manipulate the spice netlist. For example, the method 47 | set_parameters() will set or update existing parameters defined in the netlist. The method set_component_value() is 48 | used to update existing component values or models. 49 | 50 | --------------- 51 | Multiprocessing 52 | --------------- 53 | 54 | For making better use of today's computer capabilities, the SimCommander spawns several LTSpice instances 55 | each executing in parallel a simulation. 56 | 57 | By default, the number of parallel simulations is 4, however the user can override this in two ways. Either 58 | using the class constructor argument ``parallel_sims`` or by forcing the allocation of more processes in the 59 | run() call by setting ``wait_resource=False``. :: 60 | 61 | LTC.run(wait_resource=False) 62 | 63 | The recommended way is to set the parameter ``parallel_sims`` in the class constructor. :: 64 | 65 | LTC=SimCommander("my_circuit.asc", parallel_sims=8) 66 | 67 | The user then can launch a simulation with the updates done to the netlist by calling the run() method. Since the 68 | processes are not executed right away, but rather just scheduled for simulation, the wait_completion() function is 69 | needed if the user wants to execute code only after the completion of all scheduled simulations. 70 | 71 | The usage of wait_completion() is optional. Just note that the script will only end when all the scheduled tasks are 72 | executed. 73 | 74 | --------- 75 | Callbacks 76 | --------- 77 | 78 | As seen above, the `wait_completion()` can be used to wait for all the simulations to be finished. However, this is 79 | not efficient from a multiprocessor point of view. Ideally, the post-processing should be also handled while other 80 | simulations are still running. For this purpose, the user can use a function call back. 81 | 82 | The callback function is called when the simulation has finished directly by the thread that has handling the 83 | simulation. A function callback receives two arguments. 84 | The RAW file and the LOG file names. Below is an example of a callback function:: 85 | 86 | def processing_data(raw_filename, log_filename): 87 | '''This is a call back function that just prints the filenames''' 88 | print("Simulation Raw file is %s. The log is %s" % (raw_filename, log_filename) 89 | # Other code below either using LTSteps.py or raw_read.py 90 | log_info = LTSpiceLogReader(log_filename) 91 | log_info.read_measures() 92 | rise, measures = log_info.dataset["rise_time"] 93 | 94 | The callback function is optional. If no callback function is given, the thread is terminated just after the 95 | simulation is finished. 96 | """ 97 | __author__ = "Nuno Canto Brum " 98 | __copyright__ = "Copyright 2020, Fribourg Switzerland" 99 | 100 | import os 101 | from pathlib import Path 102 | from typing import Callable, Any, Union, Type 103 | import logging 104 | _logger = logging.getLogger("spicelib.SimBatch") 105 | 106 | from spicelib.editor.spice_editor import SpiceEditor 107 | from spicelib.sim.simulator import Simulator 108 | from spicelib.sim.run_task import RunTask 109 | from spicelib.sim.sim_runner import SimRunner 110 | 111 | END_LINE_TERM = '\n' 112 | 113 | 114 | class SimCommander(SpiceEditor): 115 | """ 116 | *(Deprecated)* 117 | Backwards compatibility class. 118 | 119 | This class will be soon deprecated. For a better control of the simulation environment, supporting other simulators 120 | and allowing to simulate directly the .ASC files, the SpiceEditor class is now separated from the Simulator Running 121 | class. 122 | Please check the SimRunner class for more information. 123 | """ 124 | 125 | def __init__(self, netlist_file: Union[str, Path], parallel_sims: int = 4, timeout=None, verbose=False, 126 | encoding='autodetect', simulator=None): 127 | if simulator is None: 128 | from ..sim.ltspice_simulator import LTspice # In case no simulator is given 129 | simulator = LTspice 130 | netlist_file = Path(netlist_file) 131 | self.netlist_file = netlist_file # Legacy property 132 | if netlist_file.suffix == '.asc': 133 | netlist_file = simulator.create_netlist(netlist_file) 134 | super().__init__(netlist_file, encoding) 135 | self.runner = SimRunner(simulator=simulator, parallel_sims=parallel_sims, timeout=timeout, verbose=verbose, 136 | output_folder=netlist_file.parent.as_posix()) 137 | 138 | def setLTspiceRunCommand(self, spice_tool: Union[str, Type[Simulator]]) -> None: 139 | """ 140 | *(Deprecated)* 141 | Manually setting the LTSpice run command. 142 | 143 | :param spice_tool: String containing the path to the spice tool to be used, or alternatively the Simulator 144 | object. 145 | :type spice_tool: str or Simulator 146 | :return: Nothing 147 | :rtype: None 148 | """ 149 | self.runner.set_run_command(spice_tool) 150 | 151 | def add_LTspiceRunCmdLineSwitches(self, *args) -> None: 152 | """ 153 | *(Deprecated)* 154 | Used to add an extra command line argument such as -I to add symbol search path or -FastAccess 155 | to convert the raw file into Fast Access. 156 | The arguments is a list of strings as is defined in the LTSpice command line documentation. 157 | 158 | :param args: list of strings 159 | A list of command line switches such as "-ascii" for generating a raw file in text format or "-alt" for 160 | setting the solver to alternate. See Command Line Switches information on LTSpice help file. 161 | :type args: list[str] 162 | :returns: Nothing 163 | """ 164 | self.runner.clear_command_line_switches() 165 | for option in args: 166 | self.runner.add_command_line_switch(option) 167 | 168 | def run(self, run_filename: str = None, wait_resource: bool = True, 169 | callback: Callable[[str, str], Any] = None, timeout: float = 600) -> RunTask: 170 | return self.runner.run(self, wait_resource=wait_resource, callback=callback, timeout=timeout, 171 | run_filename=run_filename) 172 | 173 | def updated_stats(self): 174 | """ 175 | *(Deprecated)* 176 | This function updates the OK/Fail statistics and releases finished RunTask objects from memory. 177 | 178 | :returns: Nothing 179 | """ 180 | self.runner.active_threads() 181 | return 182 | 183 | def wait_completion(self, timeout=None, abort_all_on_timeout=False) -> bool: 184 | return self.runner.wait_completion(timeout, abort_all_on_timeout) 185 | 186 | @property 187 | def runno(self): 188 | """*(Deprecated)* Legacy property""" 189 | return self.runner.runno 190 | 191 | @property 192 | def okSim(self): 193 | """*(Deprecated)* Legacy property""" 194 | return self.runner.okSim 195 | 196 | @property 197 | def failSim(self): 198 | """*(Deprecated)* Legacy property""" 199 | return self.runner.failSim 200 | 201 | 202 | if __name__ == "__main__": 203 | # get script absolute path 204 | meAbsPath = os.path.dirname(os.path.realpath(__file__)) 205 | meAbsPath, _ = os.path.split(meAbsPath) 206 | # select spice model 207 | LTC = SimCommander('C:\\sandbox\\PySpice\\tests\\testfile.asc') 208 | # set default arguments 209 | LTC.set_parameters(res=0.001, cap=100e-6) 210 | # define simulation 211 | LTC.add_instructions( 212 | "; Simulation settings", 213 | # [".STEP PARAM Rmotor LIST 21 28"], 214 | ".TRAN 3m", 215 | # ".step param run 1 2 1" 216 | ) 217 | # do parameter sweep 218 | for res in range(5): 219 | # LTC.runs_to_do = range(2) 220 | LTC.set_parameters(ANA=res) 221 | raw, log = LTC.run().wait_results() 222 | _logger.debug("Raw file '%s' | Log File '%s'" % (raw, log)) 223 | # Sim Statistics 224 | _logger.info('Successful/Total Simulations: ' + str(LTC.okSim) + '/' + str(LTC.runno)) 225 | 226 | 227 | def callback_function(raw_file, log_file): 228 | _logger.debug("Handling the simulation data of %s, log file %s" % (raw_file, log_file)) 229 | 230 | 231 | LTC = SimCommander(meAbsPath + "\\test_files\\testfile.asc", parallel_sims=1) 232 | tstart = 0 233 | for tstop in (2, 5, 8, 10): 234 | tduration = tstop - tstart 235 | LTC.add_instruction(".tran {}".format(tduration), ) 236 | if tstart != 0: 237 | LTC.add_instruction(".loadbias {}".format(bias_file)) 238 | # Put here your parameter modifications 239 | # LTC.set_parameters(param1=1, param2=2, param3=3) 240 | bias_file = "sim_loadbias_%d.txt" % tstop 241 | LTC.add_instruction(".savebias {} internal time={}".format(bias_file, tduration)) 242 | tstart = tstop 243 | LTC.run(callback=callback_function) 244 | 245 | LTC.reset_netlist() 246 | LTC.add_instruction('.ac dec 40 1m 1G') 247 | LTC.set_component_value('V1', 'AC 1 0') 248 | LTC.run(callback=callback_function) 249 | LTC.wait_completion() 250 | -------------------------------------------------------------------------------- /PyLTSpice/sim/sim_runner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: sim_runner.py 13 | # Purpose: Tool used to launch LTSpice simulation in batch mode. 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) 16 | # 17 | # Created: 23-12-2016 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | """ 21 | Allows launching LTSpice simulations from a Python Script, thus allowing to overcome the 3 dimensions STEP limitation on 22 | LTSpice, update resistor values, or component models. 23 | 24 | The code snipped below will simulate a circuit with two different diode models, set the simulation 25 | temperature to 80 degrees, and update the values of R1 and R2 to 3.3k. :: 26 | 27 | LTC = SimCommander("my_circuit.asc") 28 | LTC.set_parameters(temp=80) # Sets the simulation temperature to be 80 degrees 29 | LTC.set_component_value('R2', '3.3k') # Updates the resistor R2 value to be 3.3k 30 | for dmodel in ("BAT54", "BAT46WJ"): 31 | LTC.set_element_model("D1", model) # Sets the Diode D1 model 32 | for res_value in sweep(2.2, 2,4, 0.2): # Steps from 2.2 to 2.4 with 0.2 increments 33 | LTC.set_component_value('R1', res_value) # Updates the resistor R1 value to be 3.3k 34 | LTC.run() 35 | 36 | LTC.wait_completion() # Waits for the LTSpice simulations to complete 37 | 38 | print("Total Simulations: {}".format(LTC.runno)) 39 | print("Successful Simulations: {}".format(LTC.okSim)) 40 | print("Failed Simulations: {}".format(LTC.failSim)) 41 | 42 | The first line will create a python class instance that represents the LTSpice file or netlist that is to be 43 | simulated. This object implements methods that are used to manipulate the spice netlist. For example, the method 44 | set_parameters() will set or update existing parameters defined in the netlist. The method set_component_value() is 45 | used to update existing component values or models. 46 | 47 | --------------- 48 | Multiprocessing 49 | --------------- 50 | 51 | For making better use of today's computer capabilities, the SimCommander spawns several LTSpice instances 52 | each executing in parallel a simulation. 53 | 54 | By default, the number of parallel simulations is 4, however the user can override this in two ways. Either 55 | using the class constructor argument ``parallel_sims`` or by forcing the allocation of more processes in the 56 | run() call by setting ``wait_resource=False``. :: 57 | 58 | LTC.run(wait_resource=False) 59 | 60 | The recommended way is to set the parameter ``parallel_sims`` in the class constructor. :: 61 | 62 | LTC=SimCommander("my_circuit.asc", parallel_sims=8) 63 | 64 | The user then can launch a simulation with the updates done to the netlist by calling the run() method. Since the 65 | processes are not executed right away, but rather just scheduled for simulation, the wait_completion() function is 66 | needed if the user wants to execute code only after the completion of all scheduled simulations. 67 | 68 | The usage of wait_completion() is optional. Just note that the script will only end when all the scheduled tasks are 69 | executed. 70 | 71 | --------- 72 | Callbacks 73 | --------- 74 | 75 | As seen above, the `wait_completion()` can be used to wait for all the simulations to be finished. However, this is 76 | not efficient from a multiprocessor point of view. Ideally, the post-processing should be also handled while other 77 | simulations are still running. For this purpose, the user can use a function call back. 78 | 79 | The callback function is called when the simulation has finished directly by the thread that has handling the 80 | simulation. A function callback receives two arguments. 81 | The RAW file and the LOG file names. Below is an example of a callback function:: 82 | 83 | def processing_data(raw_filename, log_filename): 84 | '''This is a call back function that just prints the filenames''' 85 | print("Simulation Raw file is %s. The log is %s" % (raw_filename, log_filename) 86 | # Other code below either using LTSteps.py or raw_read.py 87 | log_info = LTSpiceLogReader(log_filename) 88 | log_info.read_measures() 89 | rise, measures = log_info.dataset["rise_time"] 90 | 91 | The callback function is optional. If no callback function is given, the thread is terminated just after the 92 | simulation is finished. 93 | """ 94 | __author__ = "Nuno Canto Brum " 95 | __copyright__ = "Copyright 2020, Fribourg Switzerland" 96 | 97 | __all__ = ['SimRunner'] 98 | 99 | from pathlib import Path 100 | 101 | import logging 102 | from typing import Union 103 | 104 | _logger = logging.getLogger("spicelib.SimRunner") 105 | 106 | from spicelib.sim.sim_runner import SimRunner as SimRunnerBase 107 | from spicelib.sim.simulator import Simulator 108 | 109 | 110 | END_LINE_TERM = '\n' 111 | 112 | 113 | class SimRunner(SimRunnerBase): 114 | """ 115 | The SimCommander class implements all the methods required for launching batches of LTSpice simulations. 116 | It takes a parameter the path to the LTSpice .asc file to be simulated, or directly the .net file. 117 | If an .asc file is given, the class will try to generate the respective .net file by calling LTspice with 118 | the --netlist option 119 | :raises FileNotFoundError: When the file is not found /!\\ This will be changed 120 | 121 | :param parallel_sims: Defines the number of parallel simulations that can be executed at the same time. Ideally this 122 | number should be aligned to the number of CPUs (processor cores) available on the machine. 123 | :type parallel_sims: int, optional 124 | :param timeout: Timeout parameter as specified on the os subprocess.run() function. Default is 600 seconds, i.e. 125 | 10 minutes. For no timeout, set to None. 126 | :type timeout: float, optional 127 | :param verbose: If True, it enables a richer printout of the program execution. 128 | :type verbose: bool, optional 129 | :param output_folder: specifying which directory shall be used for simulation files (raw and log files). 130 | :param output_folder: str 131 | :param simulator: Forcing a given simulator executable. 132 | :type simulator: str or Simulator, optional 133 | 134 | """ 135 | 136 | def __init__(self, *, simulator=None, parallel_sims: int = 4, timeout: float = 600.0, verbose=False, 137 | output_folder: str = None): 138 | # The '*' in the parameter list forces the user to use named parameters for the rest of the parameters. 139 | # This is a good practice to avoid confusion. 140 | 141 | # Gets a simulator. 142 | from ..sim.ltspice_simulator import LTspice # Used for defaults 143 | if simulator is None: 144 | simulator = LTspice 145 | elif isinstance(simulator, (str, Path)): 146 | simulator = LTspice.create_from(simulator) 147 | elif issubclass(simulator, Simulator): 148 | simulator = simulator 149 | else: 150 | simulator = LTspice 151 | super().__init__(simulator=simulator, parallel_sims=parallel_sims, timeout=timeout, verbose=verbose, 152 | output_folder=output_folder) 153 | 154 | def create_netlist(self, asc_file: Union[str, Path], cmd_line_args: list = None): 155 | """Creates a .net from an .asc using the LTSpice -netlist command line""" 156 | if not isinstance(asc_file, Path): 157 | asc_file = Path(asc_file) 158 | if asc_file.suffix == '.asc': 159 | if self.verbose: 160 | _logger.info("Creating Netlist") 161 | return self.simulator.create_netlist(asc_file, cmd_line_switches=cmd_line_args) 162 | else: 163 | _logger.warning("Unable to create the Netlist from %s" % asc_file) 164 | return None -------------------------------------------------------------------------------- /PyLTSpice/sim/sim_stepping.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # ------------------------------------------------------------------------------- 4 | # ____ _ _____ ____ _ 5 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 6 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 7 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 8 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 9 | # |___/ |_| 10 | # 11 | # Name: sim_stepping.py 12 | # Purpose: Spice Simulation Library intended to automate the exploring of 13 | # design corners, try different models and different parameter 14 | # settings. 15 | # 16 | # Author: Nuno Brum (nuno.brum@gmail.com) 17 | # 18 | # Created: 31-07-2020 19 | # Licence: refer to the LICENSE file 20 | # ------------------------------------------------------------------------------- 21 | 22 | __author__ = "Nuno Canto Brum " 23 | __copyright__ = "Copyright 2017, Fribourg Switzerland" 24 | 25 | import logging 26 | _logger = logging.getLogger("spicelib.SimStepper") 27 | _logger.info("This module is maintained for compatibility reasons." 28 | " Please use the new SimStepper class from PyLTSpice.sim.sim_stepping instead") 29 | 30 | from spicelib.sim.sim_stepping import SimStepper 31 | 32 | 33 | if __name__ == "__main__": 34 | from PyLTSpice.utils.sweep_iterators import * 35 | 36 | test = SimStepper("../../tests/DC sweep.asc") 37 | test.verbose = True 38 | test.set_parameter('R1', 3) 39 | test.add_param_sweep("res", [10, 11, 9]) 40 | test.add_value_sweep("R1", sweep_log(0.1, 10)) 41 | # test.add_model_sweep("D1", ("model1", "model2")) 42 | test.run_all() 43 | print("Finished") 44 | exit(0) 45 | -------------------------------------------------------------------------------- /PyLTSpice/sim/tookit/montecarlo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | # ------------------------------------------------------------------------------- 4 | # ____ _ _____ ____ _ 5 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 6 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 7 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 8 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 9 | # |___/ |_| 10 | # 11 | # Name: montecarlo.py 12 | # Purpose: Classes to automate Monte-Carlo simulations 13 | # 14 | # Author: Nuno Brum (nuno.brum@gmail.com) 15 | # 16 | # Created: 10-08-2023 17 | # Licence: refer to the LICENSE file 18 | # ------------------------------------------------------------------------------- 19 | 20 | from spicelib.sim.tookit.montecarlo import Montecarlo 21 | 22 | -------------------------------------------------------------------------------- /PyLTSpice/sim/tookit/worst_case.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | # ------------------------------------------------------------------------------- 4 | # ____ _ _____ ____ _ 5 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 6 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 7 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 8 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 9 | # |___/ |_| 10 | # 11 | # Name: montecarlo.py 12 | # Purpose: Classes to automate Monte-Carlo simulations 13 | # 14 | # Author: Nuno Brum (nuno.brum@gmail.com) 15 | # 16 | # Created: 10-08-2023 17 | # Licence: refer to the LICENSE file 18 | # ------------------------------------------------------------------------------- 19 | 20 | from spicelib.sim.tookit.worst_case import WorstCaseAnalysis 21 | -------------------------------------------------------------------------------- /PyLTSpice/utils/detect_encoding.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | 12 | # Name: international_support.py 13 | # Purpose: Pragmatic way to detect encoding. 14 | # 15 | # Author: Nuno Brum (nuno.brum@gmail.com) with special thanks to Fugio Yokohama (yokohama.fujio@gmail.com) 16 | # 17 | # Created: 14-05-2022 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | """ 21 | International Support functions 22 | Not using other known unicode detection libraries because we don't need something so complicated. LTSpice only supports 23 | for the time being a reduced set of encodings. 24 | """ 25 | from spicelib.utils.detect_encoding import EncodingDetectError, detect_encoding 26 | -------------------------------------------------------------------------------- /PyLTSpice/utils/sweep_iterators.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ------------------------------------------------------------------------------- 3 | # ____ _ _____ ____ _ 4 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 5 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 6 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 7 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 8 | # |___/ |_| 9 | # 10 | # Name: sweep_iterators.py 11 | # Purpose: Iterators to use for sweeping values 12 | # 13 | # Author: Nuno Brum (nuno.brum@gmail.com) 14 | # 15 | # Created: 24-07-2020 16 | # Licence: refer to the LICENSE file 17 | # 18 | # ------------------------------------------------------------------------------- 19 | 20 | from spicelib.utils.sweep_iterators import * 21 | 22 | # ======================== Andreas Kaeberlein Iterator ========================= 23 | 24 | class sweep_iterators: 25 | 26 | # ***************************** 27 | def __init__(self): 28 | """ 29 | Initialization 30 | """ 31 | self.numTotalIterations = 0 # total of iteartion if all loops are executed 32 | self.numCurrentIteration = 0 # current iteration 33 | self.iteratorEntrys = [] # list of dicts for iterator entrys 34 | self.idxForNextIter = [] # currently used entry value for loop 35 | 36 | # ***************************** 37 | 38 | # ***************************** 39 | def add(self, name="", vals=[]): 40 | """ 41 | @note adds entry to list of iterators 42 | 43 | @param name component name in ltspice schematic 44 | @param vals component values 45 | @rtype boolean 46 | @return successful 47 | """ 48 | # check for valid arguments 49 | if (0 == len(name) or 0 == len(vals)): 50 | raise ValueError("Empty arguments provided") 51 | # add to iterator list 52 | self.iteratorEntrys.append({'name': name, 'values': vals}) # add entry 53 | self.idxForNextIter.append(0) # start on first element 54 | # update total number of iteration 55 | self.numTotalIterations = 1; # prepare for mutiplication 56 | for i in self.iteratorEntrys: 57 | self.numTotalIterations = self.numTotalIterations * len(i['values']) 58 | # reset current iterator to ensure restart 59 | self.numCurrentIteration = 0 60 | # succesfull end 61 | return True 62 | 63 | # ***************************** 64 | 65 | # ***************************** 66 | def done(self): 67 | """ 68 | @note check if iteration is done 69 | @rtype boolean 70 | @retval True Iteration done 71 | @retval False Iteration needs to continue 72 | @return successful 73 | """ 74 | # check for proper init 75 | if (0 == len(self.iteratorEntrys)): 76 | return True 77 | # iteration done? 78 | if (self.numCurrentIteration < self.numTotalIterations): 79 | return False 80 | return True 81 | 82 | # ***************************** 83 | 84 | # ***************************** 85 | def next(self): 86 | """ 87 | @note creates next parameter set for sweep 88 | 89 | @rtype dict 90 | @return parameter set 91 | """ 92 | # check for iterators 93 | if (0 == len(self.iteratorEntrys)): 94 | raise ValueError("No iterator entrys defined. Use 'add' procedure") 95 | # assemble dict with new iterator values 96 | nextIter = {} 97 | for i in range(len(self.iteratorEntrys)): 98 | nextIter[self.iteratorEntrys[i]['name']] = self.iteratorEntrys[i]['values'][self.idxForNextIter[i]] 99 | # prepare for next cycle 100 | for i in range(len(self.idxForNextIter) - 1, -1, -1): 101 | # increment inner loop 102 | if (i == len(self.idxForNextIter) - 1): 103 | self.idxForNextIter[i] = self.idxForNextIter[i] + 1 104 | # inner loop overflow, inc outer loop 105 | if (self.idxForNextIter[i] >= len(self.iteratorEntrys[i]['values'])): 106 | self.idxForNextIter[i] = 0 # restart inner loop at first element 107 | self.idxForNextIter[max(i - 1, 0)] = self.idxForNextIter[i - 1] + 1 # go to next element in outer loop 108 | # increment iterator 109 | self.numCurrentIteration = self.numCurrentIteration + 1 110 | # next iteration element 111 | return nextIter 112 | # ***************************** 113 | -------------------------------------------------------------------------------- /doc/classes/asc_editor.rst: -------------------------------------------------------------------------------- 1 | AscEditor 2 | ============ 3 | 4 | Class used for manipulating LTSpice asc files. 5 | 6 | .. autoclass:: spicelib.editor.asc_editor.AscEditor 7 | :members: 8 | :undoc-members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /doc/classes/editor_classes.rst: -------------------------------------------------------------------------------- 1 | Editor Classes 2 | ============== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | spice_editor 8 | spice_circuit 9 | asc_editor 10 | -------------------------------------------------------------------------------- /doc/classes/logreader.rst: -------------------------------------------------------------------------------- 1 | LTSpiceLogReader 2 | ================ 3 | 4 | .. autoclass:: spicelib.log.ltsteps.LTSpiceLogReader 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: -------------------------------------------------------------------------------- /doc/classes/raw_classes.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Raw and Log File Classes 3 | ======================== 4 | 5 | .. toctree:: 6 | :maxdepth: 4 7 | 8 | raw_read 9 | trace 10 | logreader 11 | raw_write 12 | write_trace 13 | -------------------------------------------------------------------------------- /doc/classes/raw_read.rst: -------------------------------------------------------------------------------- 1 | RawRead 2 | ======= 3 | 4 | .. autoclass:: spicelib.raw.raw_read.RawRead 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: -------------------------------------------------------------------------------- /doc/classes/raw_write.rst: -------------------------------------------------------------------------------- 1 | RawWrite 2 | ======== 3 | 4 | .. autoclass:: spicelib.raw.raw_write.RawWrite 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: -------------------------------------------------------------------------------- /doc/classes/sim_batch.rst: -------------------------------------------------------------------------------- 1 | SimCommander 2 | ============ 3 | 4 | .. autoclass:: PyLTSpice.sim.sim_batch.SimCommander 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: -------------------------------------------------------------------------------- /doc/classes/sim_runner.rst: -------------------------------------------------------------------------------- 1 | SimRunner 2 | ============ 3 | 4 | .. autoclass:: PyLTSpice.sim.sim_runner.SimRunner 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | 10 | .. autoclass:: spicelib.sim.run_task.RunTask 11 | :members: 12 | -------------------------------------------------------------------------------- /doc/classes/sim_simulator.rst: -------------------------------------------------------------------------------- 1 | Simulators 2 | ========== 3 | 4 | .. autoclass:: spicelib.sim.simulator.Simulator 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | 10 | LTSpice 11 | ======= 12 | 13 | .. autoclass:: PyLTSpice.sim.ltspice_simulator.LTspice 14 | :members: 15 | :undoc-members: 16 | :show-inheritance: 17 | 18 | -------------------------------------------------------------------------------- /doc/classes/simulation_classes.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Simulation Classes 3 | ================== 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | sim_runner 8 | sim_simulator 9 | 10 | ============ 11 | Deprecations 12 | ============ 13 | 14 | .. toctree:: 15 | :maxdepth: 4 16 | 17 | sim_batch 18 | -------------------------------------------------------------------------------- /doc/classes/spice_circuit.rst: -------------------------------------------------------------------------------- 1 | SpiceCircuit 2 | ============ 3 | 4 | .. autoclass:: spicelib.editor.spice_editor.SpiceCircuit 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: -------------------------------------------------------------------------------- /doc/classes/spice_editor.rst: -------------------------------------------------------------------------------- 1 | SpiceEditor 2 | ============ 3 | 4 | Class used for manipulating SPICE netlists. Inherits from SpiceCircuit. 5 | 6 | .. autoclass:: spicelib.editor.spice_editor.SpiceEditor 7 | :members: 8 | :undoc-members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /doc/classes/trace.rst: -------------------------------------------------------------------------------- 1 | RawRead Trace 2 | ============= 3 | 4 | .. autoclass:: spicelib.raw.raw_classes.Axis 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | .. autoclass:: spicelib.raw.raw_classes.TraceRead 10 | :members: 11 | :undoc-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /doc/classes/write_trace.rst: -------------------------------------------------------------------------------- 1 | RawWrite Trace 2 | ============== 3 | 4 | .. autoclass:: spicelib.raw.raw_write.Trace 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | sys.path.insert(0, os.path.abspath('..')) 16 | sys.path.append(os.path.abspath('../doc/media')) 17 | 18 | print(sys.path) 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = 'PyLTSpice' 23 | copyright = '2024, Nuno Brum' 24 | author = 'Nuno Brum' 25 | 26 | release = '5.4.2' 27 | 28 | try: 29 | # Read the version from the .toml file 30 | from toml import load 31 | with open('../pyproject.toml') as f: 32 | pyproject = load(f) 33 | project = pyproject['project']['name'] 34 | release = pyproject['project']['version'] 35 | author = pyproject['project']['authors'][0]['name'] 36 | except: 37 | pass 38 | 39 | 40 | # -- General configuration --------------------------------------------------- 41 | 42 | # Add any Sphinx extension module names here, as strings. They can be 43 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 44 | # ones. 45 | extensions = [ 46 | # 'docxbuilder', 47 | # 'sphinx-docxbuilder', 48 | 'sphinx.ext.todo', 49 | 'sphinx.ext.viewcode', 50 | 'sphinx.ext.autodoc', 51 | #'sphinx.ext.autosummary', 52 | #'rinoh.frontend.sphinx' 53 | ] 54 | 55 | #autodoc_default_flags = ['members'] 56 | #autosummary_generate = True 57 | 58 | # Add any paths that contain templates here, relative to this directory. 59 | templates_path = ['_templates'] 60 | 61 | # The suffix(es) of source filenames. 62 | # You can specify multiple suffix as a list of string: 63 | # 64 | # source_suffix = ['.rst', '.md'] 65 | source_suffix = '.rst' 66 | 67 | # List of patterns, relative to source directory, that match files and 68 | # directories to ignore when looking for source files. 69 | # This pattern also affects html_static_path and html_extra_path. 70 | exclude_patterns = ['doc_build', 'Thumbs.db', '.DS_Store'] 71 | 72 | 73 | # -- Options for HTML output ------------------------------------------------- 74 | 75 | # The theme to use for HTML and HTML Help pages. See the documentation for 76 | # a list of builtin themes. 77 | # 78 | html_theme = 'agogo' 79 | html_theme_options = { 80 | 'rightsidebar' : False, 81 | } 82 | 83 | # Add any paths that contain custom static files (such as style sheets) here, 84 | # relative to this directory. They are copied after the builtin static files, 85 | # so a file named "default.css" will overwrite the builtin "default.css". 86 | # html_static_path = ['_static'] 87 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. PyLTSpice documentation master file, created by 2 | sphinx-quickstart on Thu Jul 23 10:25:30 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to PyLTSpice's documentation! 7 | ===================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | modules/modules 14 | classes/editor_classes 15 | classes/simulation_classes 16 | classes/raw_classes 17 | utilities/utilities 18 | varia/varia 19 | 20 | Indices and tables 21 | ================== 22 | 23 | * :ref:`genindex` 24 | * :ref:`modindex` 25 | * :ref:`search` 26 | -------------------------------------------------------------------------------- /doc/media/Singleton approach.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /doc/modules/SemiOpLogReader.rst: -------------------------------------------------------------------------------- 1 | Semiconductor Operating Point Reader 2 | ==================================== 3 | .. automodule:: PyLTSpice.log.semi_dev_op_reader 4 | :members: 5 | :undoc-members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /doc/modules/modules.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | Python Modules 3 | ============== 4 | 5 | PyLTSpice contains several modules for working with netlists, simulations and simulation data. 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | 10 | read_netlist 11 | run_simulations 12 | read_rawfiles 13 | read_steps 14 | write_rawfiles 15 | SemiOpLogReader 16 | sim_analysis 17 | -------------------------------------------------------------------------------- /doc/modules/read_netlist.rst: -------------------------------------------------------------------------------- 1 | Reading and Manipulating Netlists 2 | ================================= 3 | 4 | PyLTSpice has the ability to read and manipulate netlist files using the :doc:`../classes/spice_editor` class. 5 | 6 | The SpiceEditor class inherits almost all functions from :doc:`/classes/spice_circuit` class. The SpiceCircuit 7 | class allows manipulation of circuit elements such as resistors, capacitors, transistors and so on, as well as 8 | subcircuit parameters. 9 | 10 | The SpiceEditor extends these funcionalities and adds all the functions needed to read and write netlists to the disk, 11 | as well as defining top level simulation directives, such as .TRAN and .AC for example. 12 | 13 | The rationale for this division is to allow to manipulate not only elements that exist at the top level, but also to 14 | manipulate elements that exit inside of sub-circuits. In Example 2 there is small example where the value of a component 15 | inside a subcircuit is changed. 16 | 17 | When all the changes are made, the save_netlist() needs to be called in order for the updates to be registered. 18 | 19 | Do not update the original file. It is always safer to keep the original file unchanged. This helps when debuging problems, 20 | and also allows the script to revert to the initial condition by using the reset_netlist() method. 21 | 22 | 23 | Example 1: Setting parameters inside a flat netlist. 24 | 25 | .. code-block:: 26 | 27 | #read netlist 28 | import PyLTSpice 29 | net = PyLTSpice.SpiceEditor("Batch_Test.net") # Loading the Netlist 30 | 31 | net.set_parameters(res=0, cap=100e-6) # Updating parameters res and cap 32 | net.set_component_value('R2', '2k') # Updating the value of R2 to 2k 33 | net.set_component_value('R1', 4000) # Updating the value of R1 to 4k 34 | net.set_element_model('V3', "SINE(0 1 3k 0 0 0)") # changing the behaviour of V3 35 | 36 | # add instructions 37 | net.add_instructions( 38 | "; Simulation settings", 39 | ".param run = 0" 40 | ) 41 | 42 | net.save_netlist("Batch_Test_Modified.net") # writes the modified netlist to the indicated file 43 | 44 | 45 | Example 2: Updating components inside a subcircuit 46 | 47 | .. code-block:: 48 | 49 | #read netlist 50 | import PyLTSpice 51 | net = PyLTSpice.SpiceEditor("Example2.net") 52 | 53 | net.set_component_value('R1', 1000) # Sets the value of R1 to 1k 54 | net.set_component_value('XU1:Ra', '1k') # Sets the value of Ra inside of XU1 to 1k 55 | net.save_netlist("Batch_Test_Modified.net") 56 | 57 | See the class documentation for more details : 58 | 59 | * :py:class:`PyLTSpice.SpiceEditor` 60 | * :py:class:`PyLTSpice.SpiceCircuit` 61 | -------------------------------------------------------------------------------- /doc/modules/read_rawfiles.rst: -------------------------------------------------------------------------------- 1 | Reading Raw Files 2 | ================= 3 | 4 | The RawRead class is used to (surprise...) read .RAW-files. 5 | The file to read is given as parameter when creating the RawRead object. The object wil read the file and construct 6 | a structure of objects which can be used to access the data inside the .RAW-file. 7 | All traces on the .RAW-file are uploaded into memory. 8 | 9 | See :doc:`../varia/raw_file` for details of the contents of a .RAW-file. 10 | 11 | The .RAW-file contains different traces for voltages and currents in the simulation. 12 | 13 | Typically (for a transient 14 | analysis), a trace contain a value (voltage/current) for each different time point in the simulation. 15 | There is a list of time values, and a separate list of trace values for each trace. So each trace uses the same 16 | time values. 17 | If there were different steps (e.g. for a DC sweep), then there is a set of lists with time/value data for each step. 18 | 19 | Note that for an AC analysis, the traces are *frequency-versus-value* instead of *time-versus-value*. 20 | We will use 'time' as an example further in this text. 21 | 22 | The RawRead class has all the methods that allow the user to access the X-axis and trace values. If there is 23 | any stepped data (.STEP primitives), the RawRead class will try to load the log information from the same 24 | directory as the raw file in order to obtain the STEP information. 25 | 26 | You can get a list of all trace names using the ``get_trace_names()`` method. 27 | 28 | Use method ``get_trace()`` to get the trace data, which consists of values for 1 or more simulation steps. 29 | It will return a :py:class:`PyLTSpice.raw_classes.Trace` object. Use this object's ``get_wave()`` method to get 30 | the actual data points for a step. 31 | 32 | Use the method ``get_axis()`` to get the 'time' data. If there were multiple steps in the simulation, specify 33 | the number for which step you want to retrieve the time data. 34 | 35 | Now that you have lists with the times and corresponding values, you can plot this information in an X/Y plot. 36 | 37 | Note that all the data will be returned as numpy arrays. 38 | 39 | See the class documentation for more details : 40 | 41 | - :py:class:`PyLTSpice.raw_read.RawRead` 42 | - :py:class:`PyLTSpice.raw_classes.Trace` 43 | 44 | Example 45 | ------- 46 | 47 | The example below demonstrates the usage of the RawRead class. It reads a .RAW file and uses the matplotlib 48 | library to plot the results of two traces in a separate subplots. 49 | 50 | .. code-block:: 51 | 52 | from PyLTSpice import RawRead 53 | import matplotlib.pyplot as plt # use matplotlib for plotting the results 54 | 55 | raw = RawRead("some_random_file.raw") # Read the RAW file contents from disk 56 | 57 | print(raw.get_trace_names()) # Get and print a list of all the traces 58 | print(raw.get_raw_property()) # Print all the properties found in the Header section 59 | 60 | vin = raw.get_trace('V(in)') # Get the trace data 61 | vout = raw.get_trace('V(out)') # Get the second trace 62 | 63 | steps = raw.get_steps() # Get list of step numbers ([0,1,2]) for sweeped simulations 64 | # Returns [0] if there is just 1 step 65 | 66 | plt.figure() # Create the canvas for plotting 67 | 68 | _, (ax1, ax2) = plt.subplots(2, 1, sharex=True) # Create two subplots 69 | 70 | for ax in (ax1, ax2): # Use grid on both subplots 71 | ax.grid(True) 72 | 73 | plt.xlim([0.9e-3, 1.2e-3]) # Limit the X axis to just a subrange 74 | 75 | xdata = raw.get_axis() # Get the X-axis data (time) 76 | 77 | ydata = vin.get_wave() # Get all the values for the 'vin' trace 78 | ax1.plot(xdata, ydata) # Do an X/Y plot on first subplot 79 | 80 | ydata = vout.get_wave() # Get all the values for the 'vout' trace 81 | ax1.plot(xdata, ydata) # Do an X/Y plot on first subplot as well 82 | 83 | for step in steps: # On the second plot, print all the STEPS of Vout 84 | ydata = vout.get_wave(step) # Retrieve the values for this step 85 | xdata = raw.get_axis(step) # Retrieve the time vector 86 | ax2.plot(xdata, ydata) # Do X/Y plot on second subplot 87 | 88 | plt.show() # Show matplotlib's interactive window with the plots 89 | -------------------------------------------------------------------------------- /doc/modules/read_steps.rst: -------------------------------------------------------------------------------- 1 | Reading Log Information 2 | ======================== 3 | 4 | This module defines a class that can be used to parse LTSpice log files where the information about .STEP 5 | information is written. 6 | 7 | There are two possible usages of this module, either programmatically by running the utility 8 | :doc:`../utilities/LTSteps`, or by accessing data through the class as exemplified here: 9 | 10 | .. code-block:: python 11 | 12 | from PyLTSpice.log.ltsteps import LTSpiceLogReader 13 | 14 | data = LTSpiceLogReader("Batch_Test_AD820_15.log") 15 | 16 | print("Number of steps :", data.step_count) 17 | 18 | # Get the names of the variables that were stepped, and the measurements taken 19 | step_names = data.get_step_vars() 20 | meas_names = data.get_measure_names() 21 | 22 | # Print headers for a table with steps and measurements 23 | print('\t'.join([f"{step}" for step in step_names]), end='\t') 24 | print('\t'.join([f"{name}" for name in meas_names]), end='\n') 25 | 26 | # Print the data. First values of all step variables, then values of the measurements 27 | for i in range(data.step_count): 28 | print('\t'.join([f"{data[step][i]}" for step in step_names]), end='\t') 29 | print('\t'.join([f"{data[name][i]}" for name in meas_names]), end='\n') 30 | 31 | print("Total number of measurements found :", data.measure_count) 32 | 33 | For more information, see :py:class:`PyLTSpice.log.ltsteps.LTSpiceLogReader` -------------------------------------------------------------------------------- /doc/modules/run_simulations.rst: -------------------------------------------------------------------------------- 1 | Running Simulations 2 | =================== 3 | 4 | The class :py:class:`PyLTSpice.SimRunner` allows launching LTSpice simulations from a Python script, thus allowing to 5 | overcome the 3 dimensions STEP limitation on LTSpice, update resistor values, or component models. 6 | It also allows to simulate several simulations in parallel, thus speeding up the time 7 | a set of simulations would take. 8 | 9 | The class :py:class:`PyLTSpice.SpiceEditor` described in :doc:`read_netlist` allows to modify the netlist. 10 | 11 | The code snipped below will simulate a circuit with two different diode models, set the simulation 12 | temperature to 80 degrees, and update the values of R1 and R2 to 3.3k. 13 | 14 | .. code-block:: python 15 | 16 | from PyLTSpice import SimRunner, SpiceEditor, LTspice 17 | 18 | runner = SimRunner(output_folder='./temp_batch3', simulator=LTspice) # Configures the simulator to use and output 19 | # folder 20 | 21 | netlist = SpiceEditor("Batch_Test.asc") # Open the Spice Model, and creates the .net 22 | # set default arguments 23 | netlist.set_parameters(res=0, cap=100e-6) 24 | netlist.set_component_value('R2', '2k') # Modifying the value of a resistor 25 | netlist.set_component_value('R1', '4k') 26 | netlist.set_element_model('V3', "SINE(0 1 3k 0 0 0)") # Modifying the 27 | netlist.set_component_value('XU1:C2', 20e-12) # modifying a 28 | # define simulation 29 | netlist.add_instructions( 30 | "; Simulation settings", 31 | ".param run = 0" 32 | ) 33 | 34 | for opamp in ('AD712', 'AD820'): 35 | netlist.set_element_model('XU1', opamp) 36 | for supply_voltage in (5, 10, 15): 37 | netlist.set_component_value('V1', supply_voltage) 38 | netlist.set_component_value('V2', -supply_voltage) 39 | # overriding he automatic netlist naming 40 | run_netlist_file = "{}_{}_{}.net".format(netlist.netlist_file.name, opamp, supply_voltage) 41 | raw, log = runner.run_now(netlist, run_filename=run_netlist_file) 42 | # Process here the simulation results 43 | 44 | In this example we are are using the SpiceEditor instantiation 'netlist' by passing an .asc file. 45 | When receiving an .asc file, it will use LTSpice to create the corresponding .net file and read it into memory. 46 | 47 | Follows a series of function calls to 'netlist' that update the netlist in memory. 48 | The method ``set_parameters(, )`` updates the values of 49 | ``.PARAM definitions``, the method ``set_component_value(, )`` will update 50 | values of R, L and C elements, in this example we are updating only resistors. The method ``set_element_model()`` 51 | sets the values of a voltage source 'V3' and so on. 52 | 53 | Then we pass the object to the ``runner.run_now()`` method. By doing so, it will first write the netlist to the path 54 | indicated by the output folder indicated as parameter in the SimRunner instantiation, then will launch LTSpice to 55 | execute the simulation. 56 | 57 | Although this works well, it is not very efficient on the processor usage. A simulation is ended before another one is started. 58 | An alternative method to this is presented in the next section. 59 | 60 | --------------- 61 | Multiprocessing 62 | --------------- 63 | 64 | For making better use of today's computer capabilities, the SimRunner can spawn several LTSpice instances 65 | each executing in parallel a simulation. This is exemplified in the modified example below. 66 | 67 | .. code-block:: python 68 | 69 | from PyLTSpice import SimRunner, SpiceEditor, LTspice 70 | 71 | def processing_data(raw_file, log_file): 72 | """This is the function that will process the data from simulations""" 73 | print("Handling the simulation data of %s, log file %s" % (raw_file, log_file)) 74 | 75 | # Configures the simulator to use and output folder. Also defines the number of parallel simulations 76 | runner = SimRunner(output_folder='./temp_batch3', simulator=LTspice, parallel_sims=4) 77 | 78 | netlist = SpiceEditor("Batch_Test.asc") # Open the Spice Model, and creates the .net 79 | # set default arguments 80 | netlist.set_parameters(res=0, cap=100e-6) 81 | netlist.set_component_value('R2', '2k') # Modifying the value of a resistor 82 | netlist.set_component_value('R1', '4k') 83 | netlist.set_element_model('V3', "SINE(0 1 3k 0 0 0)") # Modifying the 84 | netlist.set_component_value('XU1:C2', 20e-12) # modifying a 85 | # define simulation 86 | netlist.add_instructions( 87 | "; Simulation settings", 88 | ".param run = 0" 89 | ) 90 | 91 | for opamp in ('AD712', 'AD820'): 92 | netlist.set_element_model('XU1', opamp) 93 | for supply_voltage in (5, 10, 15): 94 | netlist.set_component_value('V1', supply_voltage) 95 | netlist.set_component_value('V2', -supply_voltage) 96 | # overriding he automatic netlist naming 97 | run_netlist_file = "{}_{}_{}.net".format(netlist.netlist_file.name, opamp, supply_voltage) 98 | # This will launch up to 'parallel_sims' simulations in background before waiting for resources 99 | runner.run(netlist, run_filename=run_netlist_file, callback=processing_data) 100 | 101 | # This will wait for the all the simulations launched before to complete. 102 | runner.wait_completion() 103 | # The timeout counter is reset everytime a simulation is finished. 104 | 105 | # Sim Statistics 106 | print('Successful/Total Simulations: ' + str(runner.okSim) + '/' + str(runner.runno)) 107 | 108 | If the ``parallel_sims`` parallel simulations is not given, it defaults to 4. This means that a fifth simulation 109 | will only start when one of the other 4 is finished. If ``parallel_sims`` needs to be adjusted according to the 110 | computer capabilities. If resources are abundant, this number can be set to a higher number. If set for example 111 | to 16, it means that the 17th simulation will wait for another one to finish before starting. 112 | Another way of bypassing this behaviour is just by setting the parameter ``wait_resource=False`` to False 113 | 114 | ``runner.run(netlist, wait_resource=False)`` 115 | 116 | 117 | Finally we see in the example the ``runner.wait_completion()`` method. This method will wait for the completion 118 | of all the pending jobs. The usage of ``wait_completion()`` is recommended if the further steps on the script 119 | require that all the simulations are done. 120 | 121 | An alternative to ``wait_completion`` is to use an iterator as exemplified here: 122 | 123 | .. code-block:: python 124 | 125 | runner = SimRunner(output_folder='./temp_batch3', simulator=LTspice) # Configures the simulator to use and output 126 | # folder 127 | 128 | netlist = SpiceEditor("Batch_Test.asc") # Open the Spice Model, and creates the .net 129 | 130 | for opamp in ('AD712', 'AD820'): 131 | netlist.set_element_model('XU1', opamp) 132 | for supply_voltage in (5, 10, 15): 133 | netlist.set_component_value('V1', supply_voltage) 134 | netlist.set_component_value('V2', -supply_voltage) 135 | runner.run(netlist, run_filename=run_netlist_file) 136 | 137 | # runner.wait_completion() 138 | for raw_file, log_file in runner: 139 | if raw_file: 140 | # process the raw file information 141 | print("Processed the raw file in the main thread") 142 | 143 | print(f'Successful/Total Simulations: {runner.okSim} /{runner.runno}') 144 | 145 | 146 | --------- 147 | Callbacks 148 | --------- 149 | 150 | The methods above are alright for tasks that don't require much computational effort, or there is a small risk 151 | that the the processing fails. If this is not the case, then executing the processing of simulation results on the 152 | background thread may make sense. This not only speeds up the process, but, it also avoids crashing the program, 153 | when a simulation among hundreds fails for some reason. 154 | 155 | For this purpose, the user can define a call back function and pass it to the ``run()`` function using the callback 156 | parameter. 157 | The callback function is called when the simulation has finished directly by the thread that has handling the 158 | simulation. A function callback receives two arguments. 159 | The RAW file and the LOG file names. Below is an example of a callback function. 160 | 161 | .. code-block:: python 162 | 163 | def processing_data(raw_filename, log_filename): 164 | '''This is a call back function that just prints the filenames''' 165 | print("Simulation Raw file is %s. The log is %s" % (raw_filename, log_filename) 166 | # Other code below either using LTSteps.py or raw_read.py 167 | log_info = LTSpiceLogReader(log_filename) 168 | log_info.read_measures() 169 | rise, measures = log_info.dataset["rise_time"] 170 | return rise, measures 171 | 172 | Callback functions can be either passed directly to the run function, and they are called once the simulation is 173 | finished. 174 | 175 | There are two ways of passing a callback function depending on whether we want it to be executed as a Thread 176 | or as a Process. The key differences is that Threads are executed on the same memory space and therefore on the same 177 | core. Processes are executed in completely different memory spaces and different processor resources. Processes are 178 | slower to start, so, it's usage is only justified when parsing simulation results is really costly. 179 | 180 | The callback functions are optional. As seen in the previous sections, if no callback function is given, the thread 181 | is terminated just after the simulation is finished. 182 | 183 | ======= 184 | Threads 185 | ======= 186 | In order to use threads, it suffices to include the name of the function with the named parameter ``callback``. 187 | 188 | .. code-block:: python 189 | 190 | for supply_voltage in (5, 10, 15): 191 | netlist.set_component_value('V1', supply_voltage) 192 | netlist.set_component_value('V2', -supply_voltage) 193 | runner.run(netlist, callback=processing_data) 194 | 195 | for rise, measures in runner: 196 | print("The return of the callback function is ", rise, measures) 197 | 198 | ========= 199 | Processes 200 | ========= 201 | .. role:: underline 202 | :class: underline 203 | 204 | In order to use processes, the callback function needs to be encapsulated as a static method in a subclass of the 205 | special class called ``ProcessCallback`` and :underline:`very importantly`, all the code used to prepare and launch the 206 | simulation should be inside a ``if __name__ == "__main__":`` clause. 207 | 208 | The reason for this is that since the module is going to be imported two times, first by the python.exe __main__ 209 | function and multiple times after by python processes searching for ProcessCallback subclass. The equivalent of the 210 | previous code using processes looks like this. 211 | 212 | .. code-block:: python 213 | 214 | from PyLTSpice.sim.process_callback import ProcessCallback # Importing the ProcessCallback class type 215 | 216 | class CallbackProc(ProcessCallback): 217 | """Class encapsulating the callback function. It can have whatever name.""" 218 | 219 | @staticmethod # This decorator defines the callback as a static method, i.e., it doesn't receive the `self`. 220 | def callback(raw_file, log_file): # This function must be called callback 221 | '''This is a call back function that just prints the filenames''' 222 | print("Simulation Raw file is %s. The log is %s" % (raw_filename, log_filename) 223 | # Other code below either using LTSteps.py or raw_read.py 224 | log_info = LTSpiceLogReader(log_filename) 225 | log_info.read_measures() 226 | rise, measures = log_info.dataset["rise_time"] 227 | return rise, measures 228 | 229 | if __name__ == "__main__": # The code below must be only executed once. 230 | # Without this clause, it doesn't work. Don't forget to indent ALL the code below 231 | runner = SimRunner(output_folder='./temp', simulator=LTspice) # Configures the output folder and simulator 232 | for supply_voltage in (5, 10, 15): 233 | netlist.set_component_value('V1', supply_voltage) 234 | netlist.set_component_value('V2', -supply_voltage) 235 | runner.run(netlist, callback=CallbackProc) 236 | 237 | for rise, measures in runner: 238 | print("The return of the callback function is ", rise, measures) 239 | 240 | The ProcessCallback class which is imported from PyLTSpice.sim.process_callback already defines the __init__ function 241 | and creates all the environment for the calling and callback function, and creates the Queue used to pipe the result 242 | back to the main process. 243 | 244 | -------------------------------- 245 | Processing of simulation outputs 246 | -------------------------------- 247 | 248 | The previous sections described the way to launch simulations. The way to parse the 249 | simulation results contained in the RAW files are described in :doc:`read_rawfiles`. 250 | For parsing information contained in the LOG files, which contain information about 251 | measurements done with .MEAS primitives, is implemented by the class :py:class:`PyLTSpice.SpiceEditor` 252 | -------------------------------------------------------------------------------- /doc/modules/sallenkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/doc/modules/sallenkey.png -------------------------------------------------------------------------------- /doc/modules/sallenkey_mc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/doc/modules/sallenkey_mc.png -------------------------------------------------------------------------------- /doc/modules/sallenkey_wc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/doc/modules/sallenkey_wc.png -------------------------------------------------------------------------------- /doc/modules/sim_analysis.rst: -------------------------------------------------------------------------------- 1 | Sim Analysis Toolkit 2 | ==================== 3 | 4 | The Sim Analysis toolkit is a collection of tools for setting up, running and analyzing simulations. 5 | 6 | At present there are two main tools: 7 | * Montecarlo simulation setup 8 | * Worst Case simulation setup 9 | 10 | Other tools will be added in the future. 11 | 12 | ## Simulation Setup ## 13 | 14 | Let's consider the following circuit: 15 | 16 | .. image:: sallenkey.png 17 | :alt: Sallen-Key Amplifier 18 | :align: center 19 | :width: 710px 20 | 21 | When performing a Monte Carlo simulation on this circuit, we need to manually modify the value of each component, 22 | and then add the .step command for making several runs on the same circuit. 23 | To simplify this process, the Montecarlo class can be used as exemplified below: 24 | 25 | .. code-block:: python 26 | 27 | from PyLTSpice import AscEditor # Imports the class that manipulates the asc file 28 | from PyLTSpice.sim.tookit.montecarlo import Montecarlo # Imports the Montecarlo toolkit class 29 | 30 | sallenkey = AscEditor("./testfiles/sallenkey.asc") # Reads the asc file into memory 31 | 32 | mc = Montecarlo(sallenkey) # Instantiates the Montecarlo class, with the asc file already in memory 33 | 34 | # The following lines set the default tolerances for the components 35 | mc.set_tolerance('R', 0.01) # 1% tolerance, default distribution is uniform 36 | mc.set_tolerance('C', 0.1, distribution='uniform') # 10% tolerance, explicit uniform distribution 37 | mc.set_tolerance('V', 0.1, distribution='normal') # 10% tolerance, but using a normal distribution 38 | 39 | # Some components can have a different tolerance 40 | mc.set_tolerance('R1', 0.05) # 5% tolerance for R1 only. This only overrides the default tolerance for R1 41 | 42 | # Tolerances can be set for parameters as well 43 | mc.set_parameter_deviation('Vos', 3e-4, 5e-3, 'uniform') # The keyword 'distribution' is optional 44 | mc.prepare_testbench(1000) # Prepares the testbench for 1000 simulations 45 | 46 | # Finally the netlist is saved to a file 47 | mc.save_netlist('./testfiles/sallenkey_mc.net') 48 | 49 | When opening the created sallenkey_mc.net file, we can see that the following circuit. 50 | 51 | .. image:: sallenkey_mc.png 52 | :alt: Sallen-Key Amplifier with Montecarlo 53 | :align: center 54 | :width: 710px 55 | 56 | The following updates were made to the circuit: 57 | 58 | * The value of each component was replaced by a function that generates a random value within the specified tolerance. 59 | 60 | * The .step param run command was added to the netlist. Starts at -1 which it's the nominal value simulation, and 61 | finishes that the number of simulations specified in the prepare_testbench() method. 62 | 63 | * A default value for the run parameter was added. This is useful if the .step param run is commented out. 64 | 65 | * The R1 tolerance is different from the other resistors. This is because the tolerance was explicitly set for R1. 66 | 67 | * The Vos parameter was added to the .param list. This is because the parameter was explicitly set using the 68 | set_parameter_deviation method. 69 | 70 | * Functions utol, ntol and urng were added to the .func list. These functions are used to generate random values. 71 | 72 | 73 | Uniform distributions use the LTSpice built-in mc(x, tol) and flat(x) functions, while normal distributions use the 74 | gauss(x) function. 75 | 76 | Similarly, the worst case analysis can also be setup by using the class WorstCaseAnalysis, as exemplified below: 77 | 78 | .. code-block:: python 79 | 80 | from PyLTSpice import AscEditor # Imports the class that manipulates the asc file 81 | from PyLTSpice.sim.tookit.worst_case import WorstCaseAnalysis 82 | 83 | sallenkey = AscEditor("./testfiles/sallenkey.asc") # Reads the asc file into memory 84 | 85 | wca = WorstCaseAnalysis(sallenkey) # Instantiates the Worst Case Analysis class 86 | 87 | # The following lines set the default tolerances for the components 88 | wca.set_tolerance('R', 0.01) # 1% tolerance 89 | wca.set_tolerance('C', 0.1) # 10% tolerance 90 | wca.set_tolerance('V', 0.1) # 10% tolerance. For Worst Case analysis, the distribution is irrelevant 91 | 92 | # Some components can have a different tolerance 93 | wca.set_tolerance('R1', 0.05) # 5% tolerance for R1 only. This only overrides the default tolerance for R1 94 | 95 | # Tolerances can be set for parameters as well. 96 | wca.set_parameter_deviation('Vos', 3e-4, 5e-3) 97 | 98 | # Finally the netlist is saved to a file 99 | wca.save_netlist('./testfiles/sallenkey_wc.asc') 100 | 101 | When opening the created sallenkey_wc.net file, we can see that the following circuit. 102 | 103 | .. image:: sallenkey_wc.png 104 | :alt: Sallen-Key Amplifier with Worst Case Analysis 105 | :align: center 106 | :width: 710px 107 | 108 | 109 | The following updates were made to the circuit: 110 | 111 | * The value of each component was replaced by a function that generates a nominal, minimum and maximum value depending 112 | on the run parameter and is assigned a unique index number. (R1=0, Vos=1, R2=2, ... V2=7, VIN=8) 113 | The unique number corresponds to the bit position of the run parameter. Bit 0 corresponds to the minimum value and 114 | bit 1 corresponds to the maximum value. Calculating all possible permutations of maximum and minimum values for each 115 | component, we get 2**9 = 512 possible combinations. This maps into a 9 bit binary number, which is the run parameter. 116 | 117 | * The .step param run command was added to the netlist. It starts at -1 which it's the nominal value simulation, then 0 118 | which corresponds to the minimum value for each component, then it makes all combinations of minimum and maximum values 119 | until 511, which is the simulation with all maximum values. 120 | 121 | * A default value for the run parameter was added. This is useful if the .step param run is commented out. 122 | 123 | * The R1 tolerance is different from the other resistors. This is because the tolerance was explicitly set for R1. 124 | 125 | * The wc() function is added to the circuit. This function is used to calculate the worst case value for each component, 126 | given a tolerance value and its respective index. 127 | 128 | * The wc1() function is added to the circuit. This function is used to calculate the worst case value for each component, 129 | given a minimum and maximum value and its respective index. -------------------------------------------------------------------------------- /doc/modules/write_rawfiles.rst: -------------------------------------------------------------------------------- 1 | Writing Raw Files 2 | ================= 3 | 4 | The RawWrite class can be used to generate .RAW-files with generated or calculated data. 5 | 6 | The functionality is limited to adding traces with a single series of values. Stepped data (like with a DC sweep) is not supported. 7 | 8 | | First, generate your trace data, e.g. using numpy. 9 | | Then, make a Trace object from this data. 10 | | Then, add all Trace objects to the RawWrite object. 11 | | Lastly, write the data to a file on disk. 12 | 13 | The following example writes a RAW file with a 3 milliseconds transient simulation, 14 | containing a 10kHz sine and a 9.997kHz cosine wave. 15 | 16 | .. code-block:: 17 | 18 | import numpy as np 19 | from PyLTSpice import Trace, RawWrite 20 | 21 | LW = RawWrite() 22 | 23 | tx = Trace('time', np.arange(0.0, 3e-3, 997E-11)) 24 | vy = Trace('N001', np.sin(2 * np.pi * tx.data * 10000)) 25 | vz = Trace('N002', np.cos(2 * np.pi * tx.data * 9970)) 26 | 27 | LW.add_trace(tx) 28 | LW.add_trace(vy) 29 | LW.add_trace(vz) 30 | 31 | LW.save("test_sincos.raw") 32 | 33 | 34 | For more information, see : 35 | 36 | - :doc:`../varia/raw_file` 37 | - :py:class:`PyLTSpice.raw_write.RawWrite` 38 | - :py:class:`PyLTSpice.raw_write.Trace` 39 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | pip 2 | Sphinx 3 | numpy>=1.14.0 4 | matplotlib 5 | insipid-sphinx-theme 6 | autodocsumm -------------------------------------------------------------------------------- /doc/utilities/Histogram.rst: -------------------------------------------------------------------------------- 1 | Histogram.py 2 | ============ 3 | This module uses matplotlib to plot an histogram of a gaussian distribution and calculates the project n-sigma interval. 4 | 5 | The data can either collected from the clipboard or from a text file. Use the following command line text to call 6 | this module. 7 | 8 | .. code-block:: text 9 | 10 | python -m PyLTSpice.Histogram [options] [data_file] TRACE 11 | 12 | The help can be obtained by calling the script without arguments 13 | 14 | .. code-block:: text 15 | 16 | Usage: Histogram.py [options] LOG_FILE TRACE 17 | 18 | Options: 19 | --version show program's version number and exit 20 | -h, --help show this help message and exit 21 | -s SIGMA, --sigma=SIGMA 22 | Sigma to be used in the distribution fit. Default=3 23 | -n NBINS, --nbins=NBINS 24 | Number of bins to be used in the histogram. Default=20 25 | -c FILTERS, --condition=FILTERS 26 | Filter condition writen in python. More than one 27 | expression can be added but each expression should be 28 | preceded by -c. EXAMPLE: -c V(N001)>4 -c parameter==1 29 | -c I(V1)<0.5 30 | -f FORMAT, --format=FORMAT 31 | Format string for the X axis. Example: -f %3.4f 32 | -t TITLE, --title=TITLE 33 | Title to appear on the top of the histogram. 34 | -r RANGE, --range=RANGE 35 | Range of the X axis to use for the histogram in the 36 | form min:max. Example: -r -1:1 37 | -C, --clipboard If the data from the clipboard is to be used. 38 | -i IMAGEFILE, --image=IMAGEFILE 39 | Name of the image File. extension 'png' 40 | 41 | -------------------------------------------------------------------------------- /doc/utilities/LTSteps.rst: -------------------------------------------------------------------------------- 1 | ltsteps.exe 2 | =========== 3 | 4 | This tool allows to process data generated by LTSpice during simulation. There are three types of files that are 5 | handled by this command line tool. 6 | 7 | + log files - Files with the extension '.log' that are automatically generated during simulation, and that are 8 | normally accessible with the shortcut Ctrl+L after a simulation is ran.Log files are interesting for two reasons. 9 | 10 | 1. If .STEP primitives are used, the log file contain the correspondence between the step run and the step 11 | value configuration. 12 | 13 | 2. If .MEAS primitives are used in the schematic, the log file contains the measurements made on the output 14 | data. 15 | 16 | LTSteps.py can be used to retrieve both step and measurement information from log files. 17 | 18 | + txt files - Files exported from the Plot File -> Export data as text menu. This file is an text file where data is 19 | saved in the text format. The reason to use PyLTSpice instead of another popular lib as pandas, is because the data 20 | format when .STEPS are used in the simulation is not not very practical. The PyLTSpice LTSteps.py can be used to 21 | reformat the text, so that the run parameter is added to the data as an additional column instead of a table 22 | divider. Please Check LTSpiceExport class for more information. 23 | 24 | + mout files - Files generated by the Plot File -> Execute .MEAS Script menu. This command allows the user to run 25 | predefined .MEAS commands which create a .mout file. A .mout file has the measurement information stored in the 26 | following format: 27 | 28 | .. code-block:: text 29 | 30 | Measurement: Vout_rms 31 | step RMS(V(OUT)) FROM TO 32 | 1 1.41109 0 0.001 33 | 2 1.40729 0 0.001 34 | 35 | Measurement: Vin_rms 36 | step RMS(V(IN)) FROM TO 37 | 1 0.706221 0 0.001 38 | 2 0.704738 0 0.001 39 | 40 | Measurement: gain 41 | step Vout_rms/Vin_rms 42 | 1 1.99809 43 | 2 1.99689 44 | 45 | 46 | The LTSteps.py can be used directly from a command line by if the Python's `Scripts` folder is included in the PATH 47 | environment variable. 48 | 49 | .. code-block:: text 50 | 51 | $ ltsteps.exe 52 | 53 | If `` is a log file, it will create a file with the same name, but with extension .tout that is a 54 | tab separated value (tsv) file, which contains the .STEP and .MEAS information collected. 55 | 56 | If `` is a txt exported file, it will create a file with the same name, but with extension .tsv a 57 | tab separated value (tsv) file, which contains data reformatted with the step number as one of the columns. Please 58 | consult the reformat_LTSpice_export() function for more information. 59 | 60 | If `` is a mout file, it will create a file with the same name, but with extension .tmout that is a 61 | tab separated value (tsv) file, which contains the .MEAS information collected, but adding the STEP run information 62 | as one of the columns. 63 | 64 | If `` argument is ommited, the script will automatically search for the newest .log/.txt/.mout file 65 | and use it. 66 | -------------------------------------------------------------------------------- /doc/utilities/utilities.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Utilities 3 | ========= 4 | 5 | PyLTSpice contains some utilities that can be run using the command line. 6 | 7 | E.g.: ``python -m PyLTSpice.Histogram`` 8 | 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | 13 | LTSteps 14 | Histogram 15 | 16 | -------------------------------------------------------------------------------- /doc/varia/LTSpice.rst: -------------------------------------------------------------------------------- 1 | 2 | Installing LTspice 3 | ================== 4 | 5 | LTspice can be downloaded from : 6 | https://www.analog.com/en/design-center/design-tools-and-calculators/ltspice-simulator.html 7 | 8 | Windows 9 | +++++++ 10 | 11 | LTspice will be installed in C:\Program Files\LTC\LTspiceXVII 12 | 13 | 14 | MacOS 15 | +++++ 16 | 17 | -------------------------------------------------------------------------------- /doc/varia/raw_file.rst: -------------------------------------------------------------------------------- 1 | 2 | RAW File Structure 3 | ================== 4 | 5 | This section is written to help understand the why the structure of classes is defined as it is. 6 | 7 | The RAW file starts with a text preamble that contains information about the names of the traces the order they 8 | appear on the binary part and some extra information. 9 | In the preamble, the lines are always started by one of the following identifiers: 10 | 11 | + Title: => Contains the path of the source .asc file used to make the simulation preceded by * 12 | 13 | + Date: => Date when the simulation started 14 | 15 | + Plotname: => Name of the simulation. The known Simulation Types are: 16 | * Operation Point 17 | * DC transfer characteristic 18 | * AC Analysis 19 | * Transient Analysis 20 | * Noise Spectral Density - (V/Hz½ or A/Hz½) 21 | * Transfer Function 22 | 23 | + Flags: => Flags that are used in this plot. The simulation can have any combination of these flags. 24 | * "real" -> The traces in the raw file contain real values. As for exmple on a TRAN simulation. 25 | * "complex" -> Traces in the raw file contain complex values. As for exmple on an AC simulation. 26 | * "forward" -> Tells whether the simulation has more than one point. DC transfer 27 | characteristic, AC Analysis, Transient Analysis or Noise Spectral Density have the forward flag. 28 | Operating Point and Transfer Function don't have this flag activated. 29 | * "log" -> The preferred plot view of this data is logarithmic. 30 | * "stepped" -> The simulation had .STEP primitives. 31 | * "FastAccess" -> Order of the data is changed to speed up access. See Binary section for details. 32 | 33 | + No. Variables: => number of variables contained in this dataset. See section below for details. 34 | 35 | + No. Points: => number of points per each variable in 36 | 37 | + Offset: => when the saving of data started 38 | 39 | + Command: => Name of the simulator executable generating this file. 40 | 41 | + Backannotation: => Backannotation alerts that occurred during simulation 42 | 43 | + Variables: => a list of variable, one per line as described below 44 | 45 | + Binary: => Start of the binary section. See section below for details. 46 | 47 | Variables List 48 | -------------- 49 | The variable list contains the list of measurements saved in the raw file. The order of the variables defines how they are 50 | stored in the binary section. The format is one variable per line, using the following format: 51 | 52 | 53 | 54 | Here is an example: 55 | 56 | .. code-block:: text 57 | 58 | 0 time time 59 | 1 V(n001) voltage 60 | 2 V(n004) voltage 61 | 3 V(n003) voltage 62 | 4 V(n006) voltage 63 | 5 V(adcc) voltage 64 | 6 V(n002) voltage 65 | 7 V(3v3_m) voltage 66 | 8 V(n005) voltage 67 | 9 V(n007) voltage 68 | 10 V(24v_dsp) voltage 69 | 11 I(C3) device_current 70 | 12 I(C2) device_current 71 | 13 I(C1) device_current 72 | 14 I(I1) device_current 73 | 15 I(R4) device_current 74 | 16 I(R3) device_current 75 | 17 I(V2) device_current 76 | 18 I(V1) device_current 77 | 19 Ix(u1:+) subckt_current 78 | 20 Ix(u1:-) subckt_current 79 | 80 | Binary Section 81 | -------------- 82 | The binary section of .RAW file is where the data is usually written, unless the user had explicitly specified an ASCII 83 | representation. In this case this section is replaced with a "Values" section. 84 | LTSpice stores data directly onto the disk during simulation, writing per each time or frequency step the list of 85 | values, as exemplified below for a .TRAN simulation. 86 | 87 | ... 88 | 89 | ... 90 | 91 | ... 92 | 93 | ... 94 | 95 | ... 96 | 97 | Depending on the type of simulation the type of data changes. 98 | On TRAN simulations the timestamp is always stored as 8 bytes float (double) and trace values as a 4 bytes (single). 99 | On AC simulations the data is stored in complex format, which includes a real part and an imaginary part, each with 8 100 | bytes. 101 | The way we determine the size of the data is dividing the total block size by the number of points, then taking only 102 | the integer part. 103 | 104 | Fast Access 105 | ----------- 106 | 107 | Once a simulation is done, the user can ask LTSpice to optimize the data structure in such that variables are stored 108 | contiguously as illustrated below. 109 | 110 | ... 111 | 112 | ... 113 | 114 | ... 115 | 116 | ... 117 | 118 | ... 119 | 120 | ... 121 | 122 | This can speed up the data reading. Note that this transformation is not done automatically. Transforming data to Fast 123 | Access must be requested by the user. If the transformation is done, it is registered in the Flags: line in the 124 | header. PyLTSpice supports both Normal and Fast Access formats 125 | -------------------------------------------------------------------------------- /doc/varia/varia.rst: -------------------------------------------------------------------------------- 1 | 2 | Varia 3 | ===== 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | raw_file 9 | LTSpice 10 | 11 | -------------------------------------------------------------------------------- /examples/ltsteps_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from PyLTSpice.log.ltsteps import LTSpiceLogReader 5 | 6 | data = LTSpiceLogReader("./testfiles/Batch_Test_AD820_15.log") 7 | 8 | print("Number of steps :", data.step_count) 9 | step_names = data.get_step_vars() 10 | meas_names = data.get_measure_names() 11 | 12 | # Printing Headers 13 | print(' '.join([f"{step:15s}" for step in step_names]), end='') # Print steps names with no new line 14 | print(' '.join([f"{name:15s}" for name in meas_names]), end='\n') 15 | # Printing data 16 | for i in range(data.step_count): 17 | print(' '.join([f"{data[step][i]:15}" for step in step_names]), end='') # Print steps names with no new line 18 | print(' '.join([f"{data[name][i]:15}" for name in meas_names]), end='\n') # Print Header 19 | 20 | print("Total number of measures found :", data.measure_count) 21 | -------------------------------------------------------------------------------- /examples/raw_plotting.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import matplotlib.pyplot as plt 3 | import os 4 | from os.path import split as pathsplit 5 | from os.path import join as pathjoin 6 | import numpy as np 7 | from numpy import abs as mag, angle 8 | from PyLTSpice import RawRead 9 | 10 | 11 | def what_to_units(whattype): 12 | """Determines the unit to display on the plot Y axis""" 13 | if 'voltage' in whattype: 14 | return 'V' 15 | if 'current' in whattype: 16 | return 'A' 17 | 18 | 19 | directory = os.getcwd() 20 | 21 | if len(sys.argv) > 1: 22 | raw_filename = sys.argv[1] 23 | trace_names = sys.argv[2:] 24 | if len(trace_names) == 0: 25 | trace_names = '*' 26 | else: 27 | test_directory = './testfiles' 28 | # filename = 'DC sweep.raw' 29 | # filename = 'TRAN.raw' 30 | # filename = 'TRAN - STEP.raw' 31 | # filename = 'AC.raw' 32 | # filename = 'AC - STEP.raw' 33 | # filename = 'DC op point - STEP.raw' 34 | trace_names = ("V(out)",) 35 | filename = 'Noise.raw' 36 | trace_names = ("V(onoise)",) 37 | raw_filename = pathjoin(test_directory, filename) 38 | 39 | LTR = RawRead(raw_filename, trace_names, verbose=True) 40 | for param, value in LTR.raw_params.items(): 41 | print("{}: {}{}".format(param, " " * (20 - len(param)), str(value).strip())) 42 | 43 | if trace_names == '*': 44 | print("Reading all the traces in the raw file") 45 | trace_names = LTR.get_trace_names() 46 | 47 | traces = [LTR.get_trace(trace) for trace in trace_names] 48 | if LTR.axis is not None: 49 | steps_data = LTR.get_steps() 50 | else: 51 | steps_data = [0] 52 | print("Steps read are :", list(steps_data)) 53 | 54 | if 'complex' in LTR.flags: 55 | n_axis = len(traces) * 2 56 | else: 57 | n_axis = len(traces) 58 | 59 | fig, axis_set = plt.subplots(n_axis, 1, sharex='all') 60 | write_labels = True 61 | 62 | for i, trace in enumerate(traces): 63 | if 'complex' in LTR.flags: 64 | axises = axis_set[2 * i: 2 * i + 2] # Returns two axis 65 | else: 66 | if n_axis == 1: 67 | axises = [axis_set] # Needs to return a list 68 | else: 69 | axises = axis_set[i:i + 1] # Returns just one axis but enclosed in a list 70 | magnitude = True 71 | for ax in axises: 72 | ax.grid(True) 73 | if 'log' in LTR.flags: 74 | ax.set_xscale('log') 75 | for step_i in steps_data: 76 | if LTR.axis: 77 | x = LTR.get_axis(step_i) 78 | else: 79 | x = np.arange(LTR.nPoints) 80 | y = LTR.get_wave(trace.name, step_i) 81 | if 'complex' in LTR.flags: 82 | x = mag(x) 83 | if magnitude: 84 | ax.set_yscale('log') 85 | y = mag(y) 86 | else: 87 | y = angle(y, deg=True) 88 | if write_labels: 89 | ax.plot(x, y, label=str(steps_data[step_i])) 90 | else: 91 | ax.plot(x, y) 92 | write_labels = False 93 | 94 | if 'complex' in LTR.flags: 95 | if magnitude: 96 | title = f"{trace.name} Mag [db{what_to_units(trace.whattype)}]" 97 | magnitude = False 98 | else: 99 | title = f"{trace.name} Phase [deg]" 100 | else: 101 | title = f"{trace.name} [{what_to_units(trace.whattype)}]" 102 | ax.set_title(title) 103 | 104 | plt.figlegend() 105 | plt.show() 106 | -------------------------------------------------------------------------------- /examples/raw_read_example.py: -------------------------------------------------------------------------------- 1 | from PyLTSpice import RawRead 2 | 3 | from matplotlib import pyplot as plt 4 | 5 | LTR = RawRead("./testfiles/TRAN - STEP.raw") 6 | 7 | print(LTR.get_trace_names()) 8 | print(LTR.get_raw_property()) 9 | 10 | IR1 = LTR.get_trace("I(R1)") 11 | x = LTR.get_trace('time') # Gets the time axis 12 | steps = LTR.get_steps() 13 | for step in range(len(steps)): 14 | # print(steps[step]) 15 | plt.plot(x.get_wave(step), IR1.get_wave(step), label=steps[step]) 16 | 17 | plt.legend() # order a legend 18 | plt.show() 19 | -------------------------------------------------------------------------------- /examples/raw_write_example.py: -------------------------------------------------------------------------------- 1 | 2 | def test_readme_snippet(): 3 | # -- Start of RawWrite Example -- 4 | import numpy as np 5 | from PyLTSpice import RawRead, Trace, RawWrite 6 | LW = RawWrite(fastacces=False) 7 | tx = Trace('time', np.arange(0.0, 3e-3, 997E-11)) 8 | vy = Trace('N001', np.sin(2 * np.pi * tx.data * 10000)) 9 | vz = Trace('N002', np.cos(2 * np.pi * tx.data * 9970)) 10 | LW.add_trace(tx) 11 | LW.add_trace(vy) 12 | LW.add_trace(vz) 13 | LW.save("./testfiles/teste_snippet1.raw") 14 | # -- End of RawWrite Example -- 15 | 16 | 17 | def test_trc2raw(): # Convert Teledyne-Lecroy trace files to raw files 18 | # -- Start of Lecroy Raw File into LTspice raw file Example -- 19 | import numpy as np 20 | from PyLTSpice import RawRead, Trace, RawWrite 21 | f = open(r"./testfiles/Current_Lock_Front_Right_8V.trc") 22 | raw_type = '' # Initialization of parameters that need to be overridden by the file header 23 | wave_size = 0 24 | for line in f: 25 | tokens = line.rstrip('\r\n').split(',') 26 | if len(tokens) == 4: 27 | if tokens[0] == 'Segments' and tokens[2] == 'SegmentSize': 28 | wave_size = int(tokens[1]) * int(tokens[3]) 29 | if len(tokens) == 2: 30 | if tokens[0] == 'Time' and tokens[1] == 'Ampl': 31 | raw_type = 'transient' 32 | break 33 | if raw_type == 'transient' and wave_size > 0: 34 | data = np.genfromtxt(f, dtype='float,float', delimiter=',', max_rows=wave_size) 35 | LW = RawWrite() 36 | LW.add_trace(Trace('time', [x[0] for x in data])) 37 | LW.add_trace(Trace('Ampl', [x[1] for x in data])) 38 | LW.save("teste_trc.raw") 39 | f.close() 40 | # -- End of Lecroy Raw File into LTspice raw file Example -- 41 | 42 | 43 | def test_axis_sync(): # Test axis sync 44 | # -- Start of Combining two different time axis -- 45 | import numpy as np 46 | from PyLTSpice import RawRead, Trace, RawWrite 47 | LW = RawWrite() 48 | tx = Trace('time', np.arange(0.0, 3e-3, 997E-11)) 49 | vy = Trace('N001', np.sin(2 * np.pi * tx.data * 10000)) 50 | vz = Trace('N002', np.cos(2 * np.pi * tx.data * 9970)) 51 | LW.add_trace(tx) 52 | LW.add_trace(vy) 53 | LW.add_trace(vz) 54 | LW.save("./testfiles/teste_w.raw") 55 | LR = RawRead("./testfiles/testfile.raw") 56 | LW.add_traces_from_raw(LR, ('V(out)',), force_axis_alignment=True) 57 | LW.save("./testfiles/merge.raw") 58 | test = """ 59 | equal = True 60 | for ii in range(len(tx)): 61 | if t[ii] != tx[ii]: 62 | print(t[ii], tx[ii]) 63 | equal = False 64 | print(equal) 65 | 66 | v = LR.get_trace('N001') 67 | max_error = 1.5e-12 68 | for ii in range(len(vy)): 69 | err = abs(v[ii] - vy[ii]) 70 | if err > max_error: 71 | max_error = err 72 | print(v[ii], vy[ii], v[ii] - vy[ii]) 73 | print(max_error) 74 | """ 75 | # -- End of Combining two different Raw Files -- 76 | 77 | 78 | def test_write_ac(): 79 | # -- Start of Writing .AC raw files Example -- 80 | from PyLTSpice import RawRead, Trace, RawWrite 81 | LW = RawWrite() 82 | LR = RawRead("./testfiles/PI_Filter.raw") 83 | LR1 = RawRead("./testfiles/PI_Filter_resampled.raw") 84 | LW.add_traces_from_raw(LR, ('V(N002)',)) 85 | LW.add_traces_from_raw(LR1, 'V(N002)', rename_format='N002_resampled', force_axis_alignment=True) 86 | LW.flag_fastaccess = False 87 | LW.save("./testfiles/PI_filter_rewritten.raw") 88 | LW.flag_fastaccess = True 89 | LW.save("./testfiles/PI_filter_rewritten_fast.raw") 90 | # -- End of Writing .AC raw files Example -- 91 | 92 | 93 | def test_write_tran(): 94 | # -- Start of creating a subset of a raw file -- 95 | from PyLTSpice import RawRead, Trace, RawWrite 96 | LR = RawRead("./testfiles/TRAN - STEP.raw") 97 | LW = RawWrite() 98 | LW.add_traces_from_raw(LR, ('V(out)', 'I(C1)')) 99 | LW.flag_fastaccess = False 100 | LW.save("./testfiles/TRAN - STEP0_normal.raw") 101 | LW.flag_fastaccess = True 102 | LW.save("./testfiles/TRAN - STEP0_fast.raw") 103 | # -- End of creating a subset of a raw file -- 104 | 105 | 106 | def test_combine_tran(): 107 | # -- Start of Combining two different Raw Files -- 108 | from PyLTSpice import RawRead, RawWrite 109 | LW = RawWrite() 110 | for tag, raw in ( 111 | ("AD820_15", "./testfiles/Batch_Test_AD820_15.raw"), 112 | # ("AD820_10", "./testfiles/Batch_Test_AD820_10.raw"), 113 | ("AD712_15", "./testfiles/Batch_Test_AD712_15.raw"), 114 | # ("AD712_10", "./testfiles/Batch_Test_AD712_10.raw"), 115 | # ("AD820_5", "./testfiles/Batch_Test_AD820_5.raw"), 116 | # ("AD712_5", "./testfiles/Batch_Test_AD712_5.raw"), 117 | ): 118 | LR = RawRead(raw) 119 | LW.add_traces_from_raw(LR, ("V(out)", "I(R1)"), rename_format="{}_{tag}", tag=tag, force_axis_alignment=True) 120 | LW.flag_fastaccess = False 121 | LW.save("./testfiles/Batch_Test_Combine.raw") 122 | # -- End of Combining two different Raw Files -- 123 | 124 | 125 | test_readme_snippet() 126 | test_axis_sync() 127 | test_write_ac() 128 | test_write_tran() 129 | test_combine_tran() 130 | -------------------------------------------------------------------------------- /examples/run_montecarlo.py: -------------------------------------------------------------------------------- 1 | from PyLTSpice import AscEditor, SimRunner # Imports the class that manipulates the asc file 2 | from PyLTSpice.sim.tookit.montecarlo import Montecarlo # Imports the Montecarlo toolkit class 3 | 4 | sallenkey = AscEditor("./testfiles/sallenkey.asc") # Reads the asc file into memory 5 | runner = SimRunner(output_folder='./temp_mc') # Instantiates the runner class, with the output folder already set 6 | mc = Montecarlo(sallenkey, runner) # Instantiates the Montecarlo class, with the asc file already in memory 7 | 8 | # The following lines set the default tolerances for the components 9 | mc.set_tolerance('R', 0.01) # 1% tolerance, default distribution is uniform 10 | mc.set_tolerance('C', 0.1, distribution='uniform') # 10% tolerance, explicit uniform distribution 11 | mc.set_tolerance('V', 0.1, distribution='normal') # 10% tolerance, but using a normal distribution 12 | 13 | # Some components can have a different tolerance 14 | mc.set_tolerance('R1', 0.05) # 5% tolerance for R1 only. This only overrides the default tolerance for R1 15 | 16 | # Tolerances can be set for parameters as well 17 | mc.set_parameter_deviation('Vos', 3e-4, 5e-3, 'uniform') # The keyword 'distribution' is optional 18 | mc.prepare_testbench(num_runs=1000) # Prepares the testbench for 1000 simulations 19 | 20 | # Finally the netlist is saved to a file 21 | mc.save_netlist('./testfiles/sallenkey_mc.net') 22 | 23 | mc.run_testbench(runs_per_sim=100) # Runs the simulation with splits of 100 runs each 24 | logs = mc.read_logfiles() # Reads the log files and stores the results in the results attribute 25 | logs.obtain_amplitude_and_phase_from_complex_values() # Splits the complex values into real and imaginary parts 26 | logs.export_data('./temp_mc/data_testbench.csv') # Exports the data to a csv file 27 | logs.plot_histogram('fcut') # Plots the histograms for the results 28 | mc.cleanup_files() # Deletes the temporary files 29 | 30 | print("=====================================") 31 | # Now using the second method, where the simulations are ran one by one 32 | mc.clear_simulation_data() # Clears the simulation data 33 | mc.reset_netlist() # Resets the netlist to the original 34 | mc.run_analysis(num_runs=1000) # Runs the 1000 simulations 35 | logs = mc.read_logfiles() # Reads the log files and stores the results in the results attribute 36 | logs.export_data('./temp_mc/data_sims.csv') # Exports the data to a csv file 37 | logs.plot_histogram('fcut') # Plots the histograms for the results 38 | mc.cleanup_files() # Deletes the temporary files 39 | 40 | -------------------------------------------------------------------------------- /examples/run_worst_case.py: -------------------------------------------------------------------------------- 1 | from PyLTSpice import AscEditor, SimRunner # Imports the class that manipulates the asc file 2 | from PyLTSpice.sim.tookit.worst_case import WorstCaseAnalysis 3 | 4 | sallenkey = AscEditor("./testfiles/sallenkey.asc") # Reads the asc file into memory 5 | runner = SimRunner(output_folder='./temp_wca') # Instantiates the runner class, with the output folder already set 6 | wca = WorstCaseAnalysis(sallenkey, runner) # Instantiates the Worst Case Analysis class 7 | 8 | # The following lines set the default tolerances for the components 9 | wca.set_tolerance('R', 0.01) # 1% tolerance 10 | wca.set_tolerance('C', 0.1) # 10% tolerance 11 | wca.set_tolerance('V', 0.1) # 10% tolerance. For Worst Case analysis, the distribution is irrelevant 12 | 13 | # Some components can have a different tolerance 14 | wca.set_tolerance('R1', 0.05) # 5% tolerance for R1 only. This only overrides the default tolerance for R1 15 | 16 | # Tolerances can be set for parameters as well. 17 | wca.set_parameter_deviation('Vos', 3e-4, 5e-3) 18 | 19 | # Finally the netlist is saved to a file 20 | wca.save_netlist('./testfiles/sallenkey_wc.asc') 21 | 22 | wca.run_testbench() # Runs the simulation with splits of 100 runs each 23 | 24 | logs = wca.read_logfiles() # Reads the log files and stores the results in the results attribute 25 | logs.export_data('./temp_wca/data.csv') # Exports the data to a csv file 26 | 27 | print("Worst case results:") 28 | for param in ('fcut', 'fcut_FROM'): 29 | print(f"{param}: min:{logs.min_measure_value(param)} max:{logs.max_measure_value(param)}") 30 | 31 | wca.cleanup_files() # Deletes the temporary files 32 | -------------------------------------------------------------------------------- /examples/sim_runner_asc_example.py: -------------------------------------------------------------------------------- 1 | from PyLTSpice import SimRunner 2 | from PyLTSpice import AscEditor 3 | 4 | # Force another simulatior 5 | simulator = r"C:\Program Files\LTC\LTspiceXVII\XVIIx64.exe" 6 | 7 | # select spice model 8 | LTC = SimRunner(output_folder='./temp') 9 | 10 | netlist = AscEditor('./testfiles/Batch_Test.asc') 11 | # set default arguments 12 | netlist.set_parameters(res=0, cap=100e-6) 13 | netlist.set_component_value('R2', '2k') # Modifying the value of a resistor 14 | netlist.set_component_value('R1', '4k') 15 | netlist.set_component_value('V3', "SINE(0 1 3k 0 0 0)") 16 | 17 | netlist.add_instructions( 18 | "; Simulation settings", 19 | ";.param run = 0" 20 | ) 21 | netlist.set_parameter('run', 0) 22 | 23 | for opamp in ('AD712', 'AD820'): 24 | netlist.set_element_model('U1', opamp) 25 | for supply_voltage in (5, 10, 15): 26 | netlist.set_component_value('V1', supply_voltage) 27 | netlist.set_component_value('V2', -supply_voltage) 28 | print("simulating OpAmp", opamp, "Voltage", supply_voltage) 29 | LTC.run(netlist) 30 | 31 | for raw, log in LTC: 32 | print("Raw file: %s, Log file: %s" % (raw, log)) 33 | # do something with the data 34 | # raw_data = RawRead(raw) 35 | # log_data = LTSteps(log) 36 | # ... 37 | 38 | # Sim Statistics 39 | print('Successful/Total Simulations: ' + str(LTC.okSim) + '/' + str(LTC.runno)) 40 | 41 | enter = input("Press enter to delete created files") 42 | if enter == '': 43 | LTC.file_cleanup() 44 | -------------------------------------------------------------------------------- /examples/sim_runner_callback_example.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import logging 3 | try: 4 | from rich.logging import RichHandler 5 | except ImportError: 6 | RichHandler = None 7 | import PyLTSpice 8 | 9 | from PyLTSpice import SimRunner, AscEditor 10 | PyLTSpice.set_log_level(logging.DEBUG) 11 | if RichHandler: 12 | PyLTSpice.add_log_handler(RichHandler()) 13 | 14 | from time import sleep 15 | from random import random 16 | 17 | 18 | def processing_data(raw_file, log_file): 19 | print("Handling the simulation data of ""%s"", log file ""%s""" % (raw_file, log_file)) 20 | time_to_sleep = random() * 5 21 | print(f"Sleeping for {time_to_sleep} seconds") 22 | sleep(time_to_sleep) 23 | return "This is the result passed to the iterator" 24 | 25 | 26 | runner = SimRunner(output_folder='./temp_batch3') # Configures the simulator to use and output 27 | # folder 28 | 29 | netlist = AscEditor("./testfiles/Batch_Test.asc") # Open the Spice Model, and creates the .net 30 | # set default arguments 31 | netlist.set_parameters(res=0, cap=100e-6) 32 | netlist.set_component_value('R2', '2k') # Modifying the value of a resistor 33 | netlist.set_component_value('R1', '4k') 34 | netlist.set_element_model('V3', "SINE(0 1 3k 0 0 0)") # Modifying the 35 | netlist.set_component_value('U1:C2', 20e-12) # modifying a 36 | # define simulation 37 | netlist.add_instructions( 38 | "; Simulation settings", 39 | ";.param run = 0" 40 | ) 41 | netlist.set_parameter('run', 0) 42 | 43 | use_run_now = False 44 | 45 | for opamp in ('AD712', 'AD820'): 46 | netlist.set_element_model('U1', opamp) 47 | for supply_voltage in (5, 10, 15): 48 | netlist.set_component_value('V1', supply_voltage) 49 | netlist.set_component_value('V2', -supply_voltage) 50 | # overriding the automatic netlist naming 51 | run_netlist_file = "{}_{}_{}.net".format(netlist.circuit_file.stem, opamp, supply_voltage) 52 | if use_run_now: 53 | runner.run_now(netlist, run_filename=run_netlist_file) 54 | else: 55 | runner.run(netlist, run_filename=run_netlist_file, callback=processing_data) 56 | 57 | for results in runner: 58 | print(results) 59 | 60 | netlist.reset_netlist() 61 | netlist.add_instructions( # Adding additional instructions 62 | "; Simulation settings", 63 | ".ac dec 30 10 1Meg", 64 | ".meas AC Gain MAX mag(V(out)) ; find the peak response and call it ""Gain""", 65 | ".meas AC Fcut TRIG mag(V(out))=Gain/sqrt(2) FALL=last" 66 | ) 67 | 68 | raw, log = runner.run(netlist, run_filename="no_callback.net").wait_results() 69 | processing_data(raw, log) 70 | 71 | if use_run_now is False: 72 | results = runner.wait_completion(1, abort_all_on_timeout=True) 73 | 74 | # Sim Statistics 75 | print('Successful/Total Simulations: ' + str(runner.okSim) + '/' + str(runner.runno)) 76 | -------------------------------------------------------------------------------- /examples/sim_runner_callback_process_example.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import sys 4 | sys.path.insert(0, '..') # This is to allow the import from the PyLTSpice folder 5 | from PyLTSpice import SimRunner, SpiceEditor 6 | from PyLTSpice.sim.process_callback import ProcessCallback # Importing the ProcessCallback class type 7 | 8 | 9 | class CallbackProc(ProcessCallback): 10 | """Class encapsulating the callback function. It can have whatever name.""" 11 | 12 | @staticmethod 13 | def callback(raw_file, log_file): 14 | print("Handling the simulation data of ""%s"", log file ""%s""" % (raw_file, log_file)) 15 | # Doing some processing here 16 | return "Parsed Result of ""%s""" % raw_file + ", log file ""%s""" % log_file 17 | 18 | 19 | if __name__ == "__main__": 20 | from PyLTSpice.sim.ltspice_simulator import LTspice 21 | runner = SimRunner(output_folder='./temp_batch4', simulator=LTspice) # Configures the simulator to use and output 22 | # folder 23 | 24 | netlist = SpiceEditor("./testfiles/Batch_Test.asc") # Open the Spice Model, and creates the .net 25 | # set default arguments 26 | netlist.set_parameters(res=0, cap=100e-6) 27 | netlist.set_component_value('R2', '2k') # Modifying the value of a resistor 28 | netlist.set_component_value('R1', '4k') 29 | netlist.set_element_model('V3', "SINE(0 1 3k 0 0 0)") # Modifying the 30 | netlist.set_component_value('XU1:C2', 20e-12) # modifying a 31 | # define simulation 32 | netlist.add_instructions( 33 | "; Simulation settings", 34 | ";.param run = 0" 35 | ) 36 | netlist.set_parameter('run', 0) 37 | 38 | for opamp in ('AD712', 'AD820'): 39 | netlist.set_element_model('XU1', opamp) 40 | for supply_voltage in (5, 10, 15): 41 | netlist.set_component_value('V1', supply_voltage) 42 | netlist.set_component_value('V2', -supply_voltage) 43 | # overriding the automatic netlist naming 44 | run_netlist_file = "{}_{}_{}.net".format(netlist.netlist_file.stem, opamp, supply_voltage) 45 | runner.run(netlist, run_filename=run_netlist_file, callback=CallbackProc) 46 | 47 | for result in runner: 48 | print(result) # Prints the result of the callback function 49 | 50 | netlist.reset_netlist() 51 | netlist.add_instructions( # Adding additional instructions 52 | "; Simulation settings", 53 | ".ac dec 30 10 1Meg", 54 | ".meas AC Gain MAX mag(V(out)) ; find the peak response and call it ""Gain""", 55 | ".meas AC Fcut TRIG mag(V(out))=Gain/sqrt(2) FALL=last" 56 | ) 57 | 58 | raw, log = runner.run(netlist, run_filename="no_callback.net").wait_results() 59 | CallbackProc.callback(raw, log) 60 | 61 | results = runner.wait_completion(1, abort_all_on_timeout=True) 62 | 63 | # Sim Statistics 64 | print('Successful/Total Simulations: ' + str(runner.okSim) + '/' + str(runner.runno)) 65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/sim_runner_example.py: -------------------------------------------------------------------------------- 1 | from PyLTSpice import SimRunner 2 | from PyLTSpice import SpiceEditor 3 | 4 | # Force another simulatior 5 | simulator = r"C:\Program Files\LTC\LTspiceXVII\XVIIx64.exe" 6 | 7 | # select spice model 8 | LTC = SimRunner(output_folder='./temp') 9 | LTC.create_netlist('./testfiles/Batch_Test.asc') 10 | netlist = SpiceEditor('./testfiles/Batch_Test.net') 11 | # set default arguments 12 | netlist.set_parameters(res=0, cap=100e-6) 13 | netlist.set_component_value('R2', '2k') # Modifying the value of a resistor 14 | netlist.set_component_value('R1', '4k') 15 | netlist.set_element_model('V3', "SINE(0 1 3k 0 0 0)") # Modifying the 16 | netlist.set_component_value('XU1:C2', 20e-12) # modifying a define simulation 17 | netlist.add_instructions( 18 | "; Simulation settings", 19 | ";.param run = 0" 20 | ) 21 | netlist.set_parameter('run', 0) 22 | 23 | for opamp in ('AD712', 'AD820'): 24 | netlist.set_element_model('XU1', opamp) 25 | for supply_voltage in (5, 10, 15): 26 | netlist.set_component_value('V1', supply_voltage) 27 | netlist.set_component_value('V2', -supply_voltage) 28 | print("simulating OpAmp", opamp, "Voltage", supply_voltage) 29 | LTC.run(netlist) 30 | 31 | for raw, log in LTC: 32 | print("Raw file: %s, Log file: %s" % (raw, log)) 33 | # do something with the data 34 | # raw_data = RawRead(raw) 35 | # log_data = LTSteps(log) 36 | # ... 37 | 38 | netlist.reset_netlist() 39 | netlist.add_instructions( 40 | "; Simulation settings", 41 | ".ac dec 30 10 1Meg", 42 | ".meas AC Gain MAX mag(V(out)) ; find the peak response and call it ""Gain""", 43 | ".meas AC Fcut TRIG mag(V(out))=Gain/sqrt(2) FALL=last" 44 | ) 45 | 46 | # Sim Statistics 47 | print('Successful/Total Simulations: ' + str(LTC.okSim) + '/' + str(LTC.runno)) 48 | 49 | enter = input("Press enter to delete created files") 50 | if enter == '': 51 | LTC.file_cleanup() 52 | -------------------------------------------------------------------------------- /examples/sim_stepper_example.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from PyLTSpice import SpiceEditor, SimRunner 4 | from PyLTSpice.sim.sim_stepping import SimStepper 5 | 6 | 7 | def processing_data(raw_file, log_file): 8 | print("Handling the simulation data of %s" % log_file) 9 | 10 | 11 | # select spice model 12 | 13 | runner = SimRunner(parallel_sims=4, output_folder='./temp2') 14 | Stepper = SimStepper(SpiceEditor("./testfiles/Batch_Test.asc"), runner) 15 | # set default arguments 16 | Stepper.set_parameters(res=0, cap=100e-6) 17 | Stepper.set_component_value('R2', '2k') 18 | Stepper.set_component_value('R1', '4k') 19 | Stepper.set_element_model('V3', "SINE(0 1 3k 0 0 0)") 20 | # define simulation 21 | Stepper.add_instructions( 22 | "; Simulation settings", 23 | ";.param run = 0" 24 | ) 25 | Stepper.set_parameter('run', 0) 26 | Stepper.set_parameter('test_param2', 20) 27 | Stepper.add_model_sweep('XU1', ('AD712', 'AD820')) 28 | Stepper.add_value_sweep('V1', (5, 10, 15)) 29 | # Stepper.add_value_sweep('V1', (-5, -10, -15)) 30 | 31 | # run_netlist_file = "{}_{}_{}.net".format(Stepper.circuit_radic, opamp, supply_voltage) 32 | Stepper.run_all(callback=processing_data) 33 | 34 | # Sim Statistics 35 | print('Successful/Total Simulations: ' + str(Stepper.okSim) + '/' + str(Stepper.runno)) 36 | runner.file_cleanup() 37 | -------------------------------------------------------------------------------- /examples/spice_editor_example.py: -------------------------------------------------------------------------------- 1 | from PyLTSpice import SpiceEditor 2 | 3 | se = SpiceEditor("./testfiles/Noise.asc") 4 | 5 | se.set_component_value('R1', 11000) 6 | se.set_component_value('C1', 1.1E-6) 7 | se.set_component_value('V1', 11) 8 | 9 | se.save_netlist("./testfiles/Noise_updated.net") 10 | se.run() -------------------------------------------------------------------------------- /examples/testfiles/AC - STEP.asc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/AC - STEP.asc -------------------------------------------------------------------------------- /examples/testfiles/AC - STEP.log: -------------------------------------------------------------------------------- 1 | LTspice 24.0.11 for Windows 2 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\AC - STEP.asc 3 | Start Time: Mon May 20 13:20:59 2024 4 | solver = Normal 5 | Maximum thread count: 16 6 | 7 | 8 | 9 | .OP point found by inspection. 10 | .step r1=1000 11 | tnom = 27 12 | temp = 27 13 | method = modified trap 14 | .step r1=10000 15 | Total elapsed time: 0.037 seconds. 16 | 17 | -------------------------------------------------------------------------------- /examples/testfiles/AC - STEP.op.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/AC - STEP.op.raw -------------------------------------------------------------------------------- /examples/testfiles/AC - STEP.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/AC - STEP.raw -------------------------------------------------------------------------------- /examples/testfiles/AC - STEP_1.log: -------------------------------------------------------------------------------- 1 | LTspice 24.0.11 for Windows 2 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\AC - STEP.asc 3 | Start Time: Sun Sep 15 18:34:39 2024 4 | solver = Normal 5 | Maximum thread count: 16 6 | 7 | 8 | 9 | .OP point found by inspection. 10 | .step r1=1000 11 | tnom = 27 12 | temp = 27 13 | method = modified trap 14 | .step r1=10000 15 | Total elapsed time: 0.005 seconds. 16 | 17 | -------------------------------------------------------------------------------- /examples/testfiles/AC - STEP_1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/AC - STEP_1.raw -------------------------------------------------------------------------------- /examples/testfiles/AC.asc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/AC.asc -------------------------------------------------------------------------------- /examples/testfiles/AC.log: -------------------------------------------------------------------------------- 1 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\AC.asc 2 | 3 | .OP point found by inspection. 4 | 5 | Date: Wed Aug 9 21:48:19 2023 6 | Total elapsed time: 0.026 seconds. 7 | 8 | tnom = 27 9 | temp = 27 10 | method = trap 11 | totiter = 0 12 | traniter = 0 13 | tranpoints = 0 14 | accept = 0 15 | rejected = 0 16 | matrix size = 3 17 | fillins = 0 18 | solver = Normal 19 | Avg thread counts: 16.0/0.0/0.0/16.0 20 | Matrix Compiler1: 4 opcodes 21 | Matrix Compiler2: 15 opcodes 22 | -------------------------------------------------------------------------------- /examples/testfiles/AC.op.raw: -------------------------------------------------------------------------------- 1 | Title: * C:\sandbox\PyLTSpice\examples\testfiles\AC.asc 2 | Date: Wed Aug 9 21:48:19 2023 3 | Plotname: Operating Point 4 | Flags: real 5 | No. Variables: 5 6 | No. Points: 1 7 | Offset: 0.0000000000000000e+00 8 | Command: Linear Technology Corporation LTspice 9 | Variables: 10 | 0 V(in) voltage 11 | 1 V(out) voltage 12 | 2 I(C1) device_current 13 | 3 I(R1) device_current 14 | 4 I(Vin) device_current 15 | Binary: 16 | -------------------------------------------------------------------------------- /examples/testfiles/AC.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/AC.raw -------------------------------------------------------------------------------- /examples/testfiles/AC_1.log: -------------------------------------------------------------------------------- 1 | LTspice 24.0.11 for Windows 2 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\AC.asc 3 | Start Time: Sun Sep 15 18:34:37 2024 4 | solver = Normal 5 | Maximum thread count: 16 6 | tnom = 27 7 | temp = 27 8 | method = modified trap 9 | .OP point found by inspection. 10 | Total elapsed time: 0.023 seconds. 11 | 12 | -------------------------------------------------------------------------------- /examples/testfiles/AC_1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/AC_1.raw -------------------------------------------------------------------------------- /examples/testfiles/Batch_Test.asc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/Batch_Test.asc -------------------------------------------------------------------------------- /examples/testfiles/Batch_Test_1.log: -------------------------------------------------------------------------------- 1 | LTspice 24.0.11 for Windows 2 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\Batch_Test.asc 3 | Start Time: Sun Sep 15 18:34:45 2024 4 | solver = Normal 5 | Maximum thread count: 16 6 | tnom = 27 7 | temp = 27 8 | method = modified trap 9 | Direct Newton iteration failed to find .op point. (Use ".option noopiter" to skip.) 10 | Starting Gmin stepping 11 | Gmin = 10 12 | Gmin = 1.07374 13 | Gmin = 0.115292 14 | Gmin = 0.0123794 15 | Gmin = 0.00132923 16 | Gmin = 0.000142725 17 | Gmin = 1.5325e-05 18 | Gmin = 1.6455e-06 19 | Gmin = 1.76685e-07 20 | vernier = 0.5 21 | vernier = 0.25 22 | Gmin = 6.41063e-08 23 | vernier = 0.125 24 | vernier = 0.0625 25 | vernier = 0.0833333 26 | Gmin = 6.04858e-08 27 | vernier = 0.111111 28 | Gmin = 4.7254e-08 29 | vernier = 0.148148 30 | vernier = 0.197531 31 | Gmin = 3.13284e-08 32 | vernier = 0.263374 33 | vernier = 0.351165 34 | Gmin = 1.6399e-08 35 | vernier = 0.46822 36 | Gmin = 6.0282e-09 37 | vernier = 0.624294 38 | vernier = 0.832392 39 | Gmin = 1.18541e-09 40 | vernier = 1 41 | Gmin = 1.36264e-10 42 | Gmin = 1.46313e-11 43 | Gmin = 1.57102e-12 44 | Gmin = 1.68687e-13 45 | Gmin = 0 46 | Gmin stepping succeeded in finding the operating point. 47 | 48 | 49 | vout_rms: RMS(v(out))=1.41109 FROM 0 TO 0.001 50 | vin_rms: RMS(v(in))=0.70622 FROM 0 TO 0.001 51 | gain: vout_rms/vin_rms=1.99808 52 | vout1m: v(out)=-0.0187828 at 0.001 53 | period: time=1.49937e-10 at 1.49937e-10 54 | 55 | Total elapsed time: 0.055 seconds. 56 | 57 | -------------------------------------------------------------------------------- /examples/testfiles/Batch_Test_1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/Batch_Test_1.raw -------------------------------------------------------------------------------- /examples/testfiles/Batch_Test_AD712_15.log: -------------------------------------------------------------------------------- 1 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\Batch_Test.asc 2 | 3 | Direct Newton iteration failed to find .op point. (Use ".option noopiter" to skip.) 4 | Starting Gmin stepping 5 | Gmin = 10 6 | Gmin = 1.07374 7 | Gmin = 0.115292 8 | Gmin = 0.0123794 9 | Gmin = 0.00132923 10 | Gmin = 0.000142725 11 | Gmin = 1.5325e-05 12 | Gmin = 1.6455e-06 13 | Gmin = 1.76685e-07 14 | Gmin = 1.89714e-08 15 | Gmin = 2.03704e-09 16 | Gmin = 2.18725e-10 17 | Gmin = 2.34854e-11 18 | Gmin = 2.52173e-12 19 | Gmin = 2.70769e-13 20 | Gmin = 0 21 | Gmin stepping succeeded in finding the operating point. 22 | 23 | 24 | vout_rms: RMS(v(out))=1.0606 FROM 0 TO 0.001 25 | vin_rms: RMS(v(in))=0.707129 FROM 0 TO 0.001 26 | gain: vout_rms/vin_rms=1.49987 27 | vout1m: v(out)=-0.00129426 at 0.001 28 | period: time=0.00033338 at 0.00033338 29 | 30 | 31 | Date: Wed Aug 9 23:19:59 2023 32 | Total elapsed time: 0.210 seconds. 33 | 34 | tnom = 27 35 | temp = 27 36 | method = modified trap 37 | totiter = 2458 38 | traniter = 2092 39 | tranpoints = 1047 40 | accept = 1046 41 | rejected = 1 42 | matrix size = 11 43 | fillins = 2 44 | solver = Normal 45 | Avg thread counts: 3.8/6.6/6.6/3.8 46 | Matrix Compiler1: 566 bytes object code size 0.4/0.3/[0.2] 47 | Matrix Compiler2: 1.10 KB object code size 0.4/0.5/[0.1] 48 | 49 | -------------------------------------------------------------------------------- /examples/testfiles/Batch_Test_AD712_15.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/Batch_Test_AD712_15.raw -------------------------------------------------------------------------------- /examples/testfiles/Batch_Test_AD820_15.log: -------------------------------------------------------------------------------- 1 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\Batch_Test.asc 2 | 3 | Direct Newton iteration failed to find .op point. (Use ".option noopiter" to skip.) 4 | Starting Gmin stepping 5 | Gmin = 10 6 | Gmin = 1.07374 7 | Gmin = 0.115292 8 | Gmin = 0.0123794 9 | Gmin = 0.00132923 10 | Gmin = 0.000142725 11 | Gmin = 1.5325e-05 12 | Gmin = 1.6455e-06 13 | Gmin = 1.76685e-07 14 | vernier = 0.5 15 | vernier = 0.25 16 | Gmin = 6.41063e-08 17 | vernier = 0.125 18 | vernier = 0.166667 19 | Gmin = 4.96015e-08 20 | vernier = 0.222222 21 | vernier = 0.296296 22 | Gmin = 2.86472e-08 23 | vernier = 0.395061 24 | Gmin = 1.22325e-08 25 | vernier = 0.526748 26 | vernier = 0.702331 27 | Gmin = 3.04891e-09 28 | vernier = 0.936441 29 | vernier = 1 30 | Gmin = 3.99664e-10 31 | Gmin = 4.29136e-11 32 | Gmin = 4.60781e-12 33 | Gmin = 4.9476e-13 34 | Gmin = 0 35 | Gmin stepping succeeded in finding the operating point. 36 | 37 | 38 | vout_rms: RMS(v(out))=1.0606 FROM 0 TO 0.001 39 | vin_rms: RMS(v(in))=0.707131 FROM 0 TO 0.001 40 | gain: vout_rms/vin_rms=1.49987 41 | vout1m: v(out)=-0.00322356 at 0.001 42 | period: time=4.30399e-11 at 4.30399e-11 43 | 44 | 45 | Date: Wed Aug 9 23:20:03 2023 46 | Total elapsed time: 0.163 seconds. 47 | 48 | tnom = 27 49 | temp = 27 50 | method = modified trap 51 | totiter = 2952 52 | traniter = 2098 53 | tranpoints = 1050 54 | accept = 1048 55 | rejected = 2 56 | matrix size = 20 57 | fillins = 14 58 | solver = Normal 59 | Avg thread counts: 3.8/6.6/6.6/3.8 60 | Matrix Compiler1: 1.46 KB object code size 0.5/0.3/[0.2] 61 | Matrix Compiler2: 2.13 KB object code size 0.3/0.3/[0.2] 62 | 63 | -------------------------------------------------------------------------------- /examples/testfiles/Batch_Test_AD820_15.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/Batch_Test_AD820_15.raw -------------------------------------------------------------------------------- /examples/testfiles/DC op point - STEP.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 1240 680 3 | WIRE 144 224 96 224 4 | WIRE 240 224 224 224 5 | WIRE 272 224 240 224 6 | WIRE 336 224 272 224 7 | WIRE 448 224 416 224 8 | WIRE 480 224 448 224 9 | WIRE 544 224 480 224 10 | WIRE 656 224 624 224 11 | WIRE 688 224 656 224 12 | WIRE 752 224 688 224 13 | WIRE 864 224 832 224 14 | WIRE 896 224 864 224 15 | WIRE 960 224 896 224 16 | WIRE 1072 224 1040 224 17 | WIRE 1088 224 1072 224 18 | WIRE 1184 224 1088 224 19 | WIRE 96 240 96 224 20 | WIRE 272 240 272 224 21 | WIRE 480 240 480 224 22 | WIRE 688 240 688 224 23 | WIRE 896 240 896 224 24 | WIRE 1088 240 1088 224 25 | WIRE 96 352 96 320 26 | WIRE 272 352 272 320 27 | WIRE 272 352 96 352 28 | WIRE 480 352 480 320 29 | WIRE 480 352 272 352 30 | WIRE 688 352 688 320 31 | WIRE 688 352 480 352 32 | WIRE 896 352 896 320 33 | WIRE 896 352 688 352 34 | WIRE 1088 352 1088 320 35 | WIRE 1088 352 896 352 36 | WIRE 96 368 96 352 37 | FLAG 96 368 0 38 | FLAG 96 224 in 39 | FLAG 1072 224 b0 40 | FLAG 864 224 b1 41 | FLAG 656 224 b2 42 | FLAG 448 224 b3 43 | FLAG 240 224 b4 44 | FLAG 1184 224 out 45 | SYMBOL voltage 96 224 R0 46 | WINDOW 123 0 0 Left 0 47 | WINDOW 39 0 0 Left 0 48 | WINDOW 0 -82 53 Left 2 49 | WINDOW 3 -85 83 Left 2 50 | SYMATTR InstName Vin 51 | SYMATTR Value 1 52 | SYMBOL res 240 208 R90 53 | WINDOW 0 0 56 VBottom 2 54 | WINDOW 3 32 56 VTop 2 55 | SYMATTR InstName R1 56 | SYMATTR Value {R} 57 | SYMBOL res 256 224 R0 58 | SYMATTR InstName R2 59 | SYMATTR Value {2*R} 60 | SYMBOL res 464 224 R0 61 | SYMATTR InstName R3 62 | SYMATTR Value {2*R} 63 | SYMBOL res 432 208 R90 64 | WINDOW 0 0 56 VBottom 2 65 | WINDOW 3 32 56 VTop 2 66 | SYMATTR InstName R4 67 | SYMATTR Value {R} 68 | SYMBOL res 672 224 R0 69 | SYMATTR InstName R5 70 | SYMATTR Value {2*R} 71 | SYMBOL res 640 208 R90 72 | WINDOW 0 0 56 VBottom 2 73 | WINDOW 3 32 56 VTop 2 74 | SYMATTR InstName R6 75 | SYMATTR Value {R} 76 | SYMBOL res 880 224 R0 77 | SYMATTR InstName R7 78 | SYMATTR Value {2*R} 79 | SYMBOL res 848 208 R90 80 | WINDOW 0 0 56 VBottom 2 81 | WINDOW 3 32 56 VTop 2 82 | SYMATTR InstName R8 83 | SYMATTR Value {R} 84 | SYMBOL res 1072 224 R0 85 | SYMATTR InstName R9 86 | SYMATTR Value {R} 87 | SYMBOL res 1056 208 R90 88 | WINDOW 0 0 56 VBottom 2 89 | WINDOW 3 32 56 VTop 2 90 | SYMATTR InstName R10 91 | SYMATTR Value {R} 92 | TEXT 56 488 Left 2 !.op 93 | TEXT 56 456 Left 2 !.step Vin 1 10 1 94 | TEXT 384 448 Left 2 !.param R=1k 95 | -------------------------------------------------------------------------------- /examples/testfiles/DC op point - STEP.log: -------------------------------------------------------------------------------- 1 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\DC op point - STEP.asc 2 | 3 | Direct Newton iteration for .op point succeeded. 4 | 5 | Date: Wed Aug 9 23:01:08 2023 6 | Total elapsed time: 0.183 seconds. 7 | 8 | tnom = 27 9 | temp = 27 10 | method = trap 11 | totiter = 3 12 | traniter = 0 13 | tranpoints = 0 14 | accept = 0 15 | rejected = 0 16 | matrix size = 7 17 | fillins = 0 18 | solver = Normal 19 | Avg thread counts: 16.0/0.0/16.0/16.0 20 | Matrix Compiler1: 268 bytes object code size 21 | Matrix Compiler2: 467 bytes object code size 22 | -------------------------------------------------------------------------------- /examples/testfiles/DC op point - STEP.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/DC op point - STEP.raw -------------------------------------------------------------------------------- /examples/testfiles/DC op point - STEP_1.log: -------------------------------------------------------------------------------- 1 | LTspice 24.0.11 for Windows 2 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\DC op point - STEP.asc 3 | Start Time: Sun Sep 15 18:34:49 2024 4 | solver = Normal 5 | Maximum thread count: 16 6 | 7 | 8 | 9 | Direct Newton iteration for .op point succeeded. 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | tnom = 27 35 | temp = 27 36 | method = modified trap 37 | Total elapsed time: 0.011 seconds. 38 | 39 | -------------------------------------------------------------------------------- /examples/testfiles/DC op point - STEP_1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/DC op point - STEP_1.raw -------------------------------------------------------------------------------- /examples/testfiles/DC op point.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 880 680 3 | WIRE 208 80 128 80 4 | WIRE 288 80 208 80 5 | WIRE 288 128 288 80 6 | WIRE 128 192 128 80 7 | WIRE 288 224 288 208 8 | WIRE 384 224 288 224 9 | WIRE 288 240 288 224 10 | WIRE 128 352 128 272 11 | WIRE 288 352 288 320 12 | WIRE 288 352 128 352 13 | WIRE 128 368 128 352 14 | FLAG 128 368 0 15 | FLAG 384 224 out 16 | FLAG 208 80 in 17 | SYMBOL voltage 128 176 R0 18 | WINDOW 123 0 0 Left 0 19 | WINDOW 39 0 0 Left 0 20 | SYMATTR InstName Vin 21 | SYMATTR Value 1 22 | SYMBOL res 272 112 R0 23 | SYMATTR InstName R1 24 | SYMATTR Value 10k 25 | SYMBOL res 272 224 R0 26 | SYMATTR InstName R2 27 | SYMATTR Value 10k 28 | TEXT 94 392 Left 2 !.op 29 | -------------------------------------------------------------------------------- /examples/testfiles/DC op point_1.log: -------------------------------------------------------------------------------- 1 | LTspice 24.0.11 for Windows 2 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\DC op point.asc 3 | Start Time: Sun Sep 15 18:34:47 2024 4 | solver = Normal 5 | Maximum thread count: 16 6 | tnom = 27 7 | temp = 27 8 | method = modified trap 9 | Direct Newton iteration for .op point succeeded. 10 | Total elapsed time: 0.004 seconds. 11 | 12 | -------------------------------------------------------------------------------- /examples/testfiles/DC op point_1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/DC op point_1.raw -------------------------------------------------------------------------------- /examples/testfiles/DC sweep.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 880 680 3 | WIRE 208 80 128 80 4 | WIRE 288 80 208 80 5 | WIRE 288 128 288 80 6 | WIRE 128 192 128 80 7 | WIRE 288 224 288 208 8 | WIRE 368 224 288 224 9 | WIRE 448 224 368 224 10 | WIRE 288 240 288 224 11 | WIRE 128 352 128 272 12 | WIRE 288 352 288 320 13 | WIRE 288 352 128 352 14 | WIRE 448 352 448 288 15 | WIRE 448 352 288 352 16 | WIRE 128 368 128 352 17 | FLAG 128 368 0 18 | FLAG 368 224 out 19 | FLAG 208 80 in 20 | SYMBOL voltage 128 176 R0 21 | WINDOW 123 0 0 Left 0 22 | WINDOW 39 0 0 Left 0 23 | WINDOW 0 -82 53 Left 2 24 | WINDOW 3 -85 83 Left 2 25 | SYMATTR InstName Vin 26 | SYMATTR Value 1 27 | SYMBOL res 272 112 R0 28 | SYMATTR InstName R1 29 | SYMATTR Value 10k 30 | SYMBOL res 272 224 R0 31 | SYMATTR InstName R2 32 | SYMATTR Value {res} 33 | SYMBOL diode 432 224 R0 34 | WINDOW 0 39 36 Left 2 35 | SYMATTR InstName D1 36 | TEXT 56 488 Left 2 !.dc Vin 1 10 9 37 | TEXT 56 440 Left 2 !.param res=10k 38 | TEXT 120 400 Left 2 !.step temp 0 100 50 39 | TEXT 408 400 Left 2 ;.step param res 1k 16k 5k 40 | TEXT 56 512 Left 2 !.op 41 | TEXT 296 488 Left 2 !.param temp = 0 42 | -------------------------------------------------------------------------------- /examples/testfiles/DC sweep.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/DC sweep.raw -------------------------------------------------------------------------------- /examples/testfiles/Noise.asc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/Noise.asc -------------------------------------------------------------------------------- /examples/testfiles/Noise.log: -------------------------------------------------------------------------------- 1 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\Noise.asc 2 | 3 | Direct Newton iteration for .op point succeeded. 4 | 5 | Date: Wed Aug 9 23:16:06 2023 6 | Total elapsed time: 0.048 seconds. 7 | 8 | tnom = 27 9 | temp = 27 10 | method = trap 11 | totiter = 3 12 | traniter = 0 13 | tranpoints = 0 14 | accept = 0 15 | rejected = 0 16 | matrix size = 3 17 | fillins = 0 18 | solver = Normal 19 | Avg thread counts: 16.0/0.0/16.0/13.2 20 | Matrix Compiler1: 3 opcodes 21 | Matrix Compiler2: 175 bytes object code size 22 | -------------------------------------------------------------------------------- /examples/testfiles/Noise.op.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/Noise.op.raw -------------------------------------------------------------------------------- /examples/testfiles/Noise.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/Noise.raw -------------------------------------------------------------------------------- /examples/testfiles/Noise_updated.net: -------------------------------------------------------------------------------- 1 | * C:\sandbox\PyLTSpice\examples\testfiles\Noise.asc 2 | V1 in 0 11 3 | R1 out in 11k 4 | C1 out 0 1.1u 5 | .noise V(out) V1 oct 10 1m 10Meg 6 | .backanno 7 | .end 8 | -------------------------------------------------------------------------------- /examples/testfiles/PI_Filter.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/PI_Filter.raw -------------------------------------------------------------------------------- /examples/testfiles/PI_Filter_resampled.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/PI_Filter_resampled.raw -------------------------------------------------------------------------------- /examples/testfiles/TRAN - STEP.asc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/TRAN - STEP.asc -------------------------------------------------------------------------------- /examples/testfiles/TRAN - STEP.log: -------------------------------------------------------------------------------- 1 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\TRAN - STEP.asc 2 | 3 | .OP point found by inspection. 4 | .step vin=1 r1=1000 5 | .step vin=10 r1=1000 6 | .step vin=1 r1=10000 7 | .step vin=10 r1=10000 8 | 9 | 10 | Measurement: t1 11 | step v(out) at 12 | 1 0.632016 0.001 13 | 2 6.31901 0.001 14 | 3 0.094948 0.001 15 | 4 0.950013 0.001 16 | 17 | Measurement: t2 18 | step v(out) at 19 | 1 0.864411 0.002 20 | 2 8.64432 0.002 21 | 3 0.180818 0.002 22 | 4 1.81244 0.002 23 | 24 | Measurement: t3 25 | step v(out) at 26 | 1 0.950097 0.003 27 | 2 9.50098 0.003 28 | 3 0.258697 0.003 29 | 4 2.58607 0.003 30 | 31 | Measurement: t4 32 | step v(out) at 33 | 1 0.981639 0.004 34 | 2 9.81665 0.004 35 | 3 0.329238 0.004 36 | 4 3.28877 0.004 37 | 38 | Measurement: t5 39 | step v(out) at 40 | 1 0.993262 0.005 41 | 2 9.93262 0.005 42 | 3 0.393469 0.005 43 | 4 3.93469 0.005 44 | 45 | 46 | Date: Wed Aug 9 23:29:11 2023 47 | Total elapsed time: 0.357 seconds. 48 | 49 | tnom = 27 50 | temp = 27 51 | method = modified trap 52 | totiter = 2162 53 | traniter = 2162 54 | tranpoints = 1082 55 | accept = 1081 56 | rejected = 1 57 | matrix size = 3 58 | fillins = 0 59 | solver = Normal 60 | Avg thread counts: 3.7/6.4/6.4/3.7 61 | Matrix Compiler1: 3 opcodes 0.1/[0.0]/0.1 62 | Matrix Compiler2: off [0.0]/0.1/0.1 63 | 64 | -------------------------------------------------------------------------------- /examples/testfiles/TRAN - STEP.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/TRAN - STEP.raw -------------------------------------------------------------------------------- /examples/testfiles/TRAN - STEP_1.log: -------------------------------------------------------------------------------- 1 | LTspice 24.0.11 for Windows 2 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\TRAN - STEP.asc 3 | Start Time: Sun Sep 15 18:35:00 2024 4 | solver = Normal 5 | Maximum thread count: 16 6 | 7 | 8 | 9 | .OP point found by inspection. 10 | .step vin=1 r1=1000 11 | 12 | 13 | 14 | .step vin=10 r1=1000 15 | 16 | 17 | 18 | .step vin=1 r1=10000 19 | tnom = 27 20 | temp = 27 21 | method = modified trap 22 | .step vin=10 r1=10000 23 | 24 | 25 | Measurement: t1 26 | step v(out) at 27 | 1 0.632016 0.001 28 | 2 6.31901 0.001 29 | 3 0.094948 0.001 30 | 4 0.950013 0.001 31 | 32 | Measurement: t2 33 | step v(out) at 34 | 1 0.864411 0.002 35 | 2 8.64432 0.002 36 | 3 0.180818 0.002 37 | 4 1.81244 0.002 38 | 39 | Measurement: t3 40 | step v(out) at 41 | 1 0.950097 0.003 42 | 2 9.50098 0.003 43 | 3 0.258697 0.003 44 | 4 2.58607 0.003 45 | 46 | Measurement: t4 47 | step v(out) at 48 | 1 0.981639 0.004 49 | 2 9.81665 0.004 50 | 3 0.329238 0.004 51 | 4 3.28877 0.004 52 | 53 | Measurement: t5 54 | step v(out) at 55 | 1 0.993262 0.005 56 | 2 9.93262 0.005 57 | 3 0.393469 0.005 58 | 4 3.93469 0.005 59 | 60 | Total elapsed time: 0.023 seconds. 61 | 62 | -------------------------------------------------------------------------------- /examples/testfiles/TRAN - STEP_1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/TRAN - STEP_1.raw -------------------------------------------------------------------------------- /examples/testfiles/TRAN.asc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/TRAN.asc -------------------------------------------------------------------------------- /examples/testfiles/TRAN.log: -------------------------------------------------------------------------------- 1 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\TRAN.asc 2 | 3 | .OP point found by inspection. 4 | 5 | t1: v(out)=0.63174 at 0.001 6 | t2: v(out)=0.864813 at 0.002 7 | t3: v(out)=0.950225 at 0.003 8 | t4: v(out)=0.981665 at 0.004 9 | t5: v(out)=0.993262 at 0.005 10 | 11 | 12 | Date: Wed Aug 9 21:46:53 2023 13 | Total elapsed time: 0.098 seconds. 14 | 15 | tnom = 27 16 | temp = 27 17 | method = modified trap 18 | totiter = 2162 19 | traniter = 2162 20 | tranpoints = 1082 21 | accept = 1081 22 | rejected = 1 23 | matrix size = 3 24 | fillins = 0 25 | solver = Normal 26 | Avg thread counts: 3.7/6.4/6.4/3.7 27 | Matrix Compiler1: 3 opcodes 0.1/[0.1]/0.1 28 | Matrix Compiler2: 175 bytes object code size 0.1/0.1/[0.1] 29 | 30 | -------------------------------------------------------------------------------- /examples/testfiles/TRAN.op.raw: -------------------------------------------------------------------------------- 1 | Title: * C:\sandbox\PyLTSpice\examples\testfiles\TRAN.asc 2 | Date: Wed Aug 9 21:46:52 2023 3 | Plotname: Operating Point 4 | Flags: real 5 | No. Variables: 5 6 | No. Points: 1 7 | Offset: 0.0000000000000000e+00 8 | Command: Linear Technology Corporation LTspice 9 | Variables: 10 | 0 V(in) voltage 11 | 1 V(out) voltage 12 | 2 I(C1) device_current 13 | 3 I(R1) device_current 14 | 4 I(Vin) device_current 15 | Binary: 16 | -------------------------------------------------------------------------------- /examples/testfiles/TRAN.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/TRAN.raw -------------------------------------------------------------------------------- /examples/testfiles/TRAN_1.log: -------------------------------------------------------------------------------- 1 | LTspice 24.0.11 for Windows 2 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\TRAN.asc 3 | Start Time: Sun Sep 15 18:34:58 2024 4 | solver = Normal 5 | Maximum thread count: 16 6 | tnom = 27 7 | temp = 27 8 | method = modified trap 9 | .OP point found by inspection. 10 | 11 | t1: v(out)=0.63174 at 0.001 12 | t2: v(out)=0.864813 at 0.002 13 | t3: v(out)=0.950225 at 0.003 14 | t4: v(out)=0.981665 at 0.004 15 | t5: v(out)=0.993262 at 0.005 16 | 17 | Total elapsed time: 0.009 seconds. 18 | 19 | -------------------------------------------------------------------------------- /examples/testfiles/TRAN_1.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/TRAN_1.raw -------------------------------------------------------------------------------- /examples/testfiles/diode.asy: -------------------------------------------------------------------------------- 1 | Version 4 2 | SymbolType CELL 3 | LINE Normal 0 44 32 44 4 | LINE Normal 0 20 32 20 5 | LINE Normal 32 20 16 44 6 | LINE Normal 0 20 16 44 7 | LINE Normal 16 0 16 20 8 | LINE Normal 16 44 16 64 9 | WINDOW 0 24 0 Left 2 10 | WINDOW 3 24 64 Left 2 11 | SYMATTR Value D 12 | SYMATTR Prefix D 13 | SYMATTR Description Diode 14 | PIN 16 0 NONE 0 15 | PINATTR PinName + 16 | PINATTR SpiceOrder 1 17 | PIN 16 64 NONE 0 18 | PINATTR PinName - 19 | PINATTR SpiceOrder 2 20 | -------------------------------------------------------------------------------- /examples/testfiles/res.asy: -------------------------------------------------------------------------------- 1 | Version 4 2 | SymbolType CELL 3 | LINE Normal 16 88 16 96 4 | LINE Normal 0 80 16 88 5 | LINE Normal 32 64 0 80 6 | LINE Normal 0 48 32 64 7 | LINE Normal 32 32 0 48 8 | LINE Normal 16 16 16 24 9 | LINE Normal 16 24 32 32 10 | WINDOW 0 36 40 Left 2 11 | WINDOW 3 36 76 Left 2 12 | SYMATTR Value R 13 | SYMATTR Prefix R 14 | SYMATTR Description A resistor 15 | PIN 16 16 NONE 0 16 | PINATTR PinName A 17 | PINATTR SpiceOrder 1 18 | PIN 16 96 NONE 0 19 | PINATTR PinName B 20 | PINATTR SpiceOrder 2 21 | -------------------------------------------------------------------------------- /examples/testfiles/sallenkey.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 1160 680 3 | WIRE 448 -256 208 -256 4 | WIRE 592 -256 512 -256 5 | WIRE 592 -192 592 -256 6 | WIRE 592 -192 336 -192 7 | WIRE 864 -192 816 -192 8 | WIRE 816 -176 816 -192 9 | WIRE 336 -80 336 -192 10 | WIRE 400 -80 336 -80 11 | WIRE 528 -64 464 -64 12 | WIRE 592 -64 592 -192 13 | WIRE 592 -64 528 -64 14 | WIRE 672 -64 592 -64 15 | WIRE 16 -48 0 -48 16 | WIRE 96 -48 16 -48 17 | WIRE 208 -48 208 -256 18 | WIRE 208 -48 176 -48 19 | WIRE 224 -48 208 -48 20 | WIRE 352 -48 304 -48 21 | WIRE 400 -48 352 -48 22 | WIRE 672 -32 672 -64 23 | WIRE 352 0 352 -48 24 | WIRE 672 96 672 48 25 | FLAG 816 -176 0 26 | FLAG 864 -272 VCC 27 | FLAG 864 -112 VEE 28 | FLAG 0 32 0 29 | FLAG 16 -48 VI 30 | FLAG 528 -64 VO 31 | FLAG 672 96 0 32 | FLAG 352 64 0 33 | FLAG 432 -32 VEE 34 | FLAG 432 -96 VCC 35 | SYMBOL voltage 864 -288 R0 36 | WINDOW 0 43 26 Left 2 37 | WINDOW 3 40 53 Left 2 38 | WINDOW 123 0 0 Left 2 39 | WINDOW 39 0 0 Left 2 40 | SYMATTR InstName V1 41 | SYMATTR Value 15 42 | SYMBOL voltage 864 -208 R0 43 | WINDOW 0 40 54 Left 2 44 | WINDOW 3 40 78 Left 2 45 | WINDOW 123 0 0 Left 2 46 | WINDOW 39 0 0 Left 2 47 | SYMATTR InstName V2 48 | SYMATTR Value 15 49 | SYMBOL res 320 -64 R90 50 | WINDOW 0 0 56 VBottom 2 51 | WINDOW 3 32 56 VTop 2 52 | SYMATTR InstName R2 53 | SYMATTR Value 10k 54 | SYMBOL res 192 -64 R90 55 | WINDOW 0 0 56 VBottom 2 56 | WINDOW 3 32 56 VTop 2 57 | SYMATTR InstName R1 58 | SYMATTR Value 10k 59 | SYMBOL cap 512 -272 R90 60 | WINDOW 0 0 32 VBottom 2 61 | WINDOW 3 32 32 VTop 2 62 | SYMATTR InstName C1 63 | SYMATTR Value 1n 64 | SYMBOL res 656 -48 R0 65 | SYMATTR InstName R4 66 | SYMATTR Value 1Meg 67 | SYMBOL cap 368 64 R180 68 | WINDOW 0 24 56 Left 2 69 | WINDOW 3 24 8 Left 2 70 | SYMATTR InstName C2 71 | SYMATTR Value 1n 72 | SYMBOL voltage 0 -64 R0 73 | WINDOW 0 -76 52 Left 2 74 | WINDOW 3 24 152 Left 2 75 | WINDOW 123 24 124 Left 2 76 | WINDOW 39 0 0 Left 0 77 | SYMATTR InstName V3 78 | SYMATTR Value 0 79 | SYMATTR Value2 AC 1 0 80 | SYMBOL OpAmps\\UniversalOpAmp 432 -64 R0 81 | WINDOW 123 -218 187 Left 2 82 | SYMATTR InstName U2 83 | SYMATTR Value2 Avol=1Meg GBW=10Meg Vos={Vos} 84 | TEXT -56 96 Left 2 !.ac dec 10 1 10Meg 85 | TEXT -56 200 Left 2 !.meas AC Gain MAX mag(V(VO)) 86 | TEXT -56 232 Left 2 !.meas AC Fcut TRIG mag(V(VO))=Gain/sqrt(2) FALL=last 87 | TEXT 208 160 Left 2 !.param Vos=0 88 | -------------------------------------------------------------------------------- /examples/testfiles/sub_circuit.asy: -------------------------------------------------------------------------------- 1 | Version 4 2 | SymbolType BLOCK 3 | LINE Normal 49 -64 1 -32 4 | LINE Normal 80 -64 49 -64 5 | LINE Normal 128 -32 80 -64 6 | LINE Normal 128 -32 128 -32 7 | RECTANGLE Normal 128 32 0 -32 8 | CIRCLE Normal 86 -53 45 -65 9 | ARC Normal 36 -18 92 -54 60 -7 58 -60 10 | PIN 0 0 LEFT 8 11 | PINATTR PinName IN 12 | PINATTR SpiceOrder 1 13 | PIN 128 0 RIGHT 8 14 | PINATTR PinName OUT 15 | PINATTR SpiceOrder 2 16 | -------------------------------------------------------------------------------- /examples/testfiles/temp/readme.txt: -------------------------------------------------------------------------------- 1 | This is only included as a dump for the test files executed during unittests -------------------------------------------------------------------------------- /examples/testfiles/testfile.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 880 680 3 | WIRE 224 -32 192 -32 4 | WIRE 336 -32 288 -32 5 | WIRE 144 80 128 80 6 | WIRE 192 80 192 -32 7 | WIRE 192 80 144 80 8 | WIRE 224 80 192 80 9 | WIRE 336 80 336 -32 10 | WIRE 336 80 304 80 11 | WIRE 384 80 336 80 12 | WIRE 448 80 384 80 13 | WIRE 448 128 448 80 14 | WIRE 128 144 128 80 15 | WIRE 128 256 128 224 16 | WIRE 448 256 448 192 17 | FLAG 128 256 0 18 | FLAG 448 256 0 19 | FLAG 384 80 out 20 | FLAG 144 80 in 21 | SYMBOL voltage 128 128 M0 22 | WINDOW 123 0 0 Left 0 23 | WINDOW 39 0 0 Left 0 24 | SYMATTR InstName V1 25 | SYMATTR Value PULSE(-1 1 1u 1n 1n 2m 1m 1) 26 | SYMBOL res 320 64 R90 27 | WINDOW 0 0 56 VBottom 2 28 | WINDOW 3 32 56 VTop 2 29 | SYMATTR InstName R1 30 | SYMATTR Value {res} 31 | SYMBOL cap 432 128 R0 32 | SYMATTR InstName C1 33 | SYMATTR Value {cap} 34 | SYMBOL cap 288 -48 R90 35 | WINDOW 0 0 32 VBottom 2 36 | WINDOW 3 32 32 VTop 2 37 | SYMATTR InstName C2 38 | SYMATTR Value {cap} 39 | TEXT -248 280 Left 2 !.tran 3m 40 | TEXT 152 -168 Left 2 !.param cap=1n res=1k 41 | TEXT 288 -120 Left 2 !.step dec param cap 1p 10u 1 42 | -------------------------------------------------------------------------------- /examples/testfiles/testfile.log: -------------------------------------------------------------------------------- 1 | Circuit: * C:\sandbox\PyLTSpice\examples\testfiles\testfile.asc 2 | 3 | WARNING: Specified period is not longer than the sum of Trise, Tfall, and Ton for v1. Increasing period to 0.002 4 | Direct Newton iteration for .op point succeeded. 5 | .step cap=1e-12 6 | .step cap=1e-11 7 | .step cap=1e-10 8 | .step cap=1e-09 9 | .step cap=1e-08 10 | .step cap=1e-07 11 | .step cap=1e-06 12 | .step cap=1e-05 13 | 14 | Date: Wed Aug 9 23:28:30 2023 15 | Total elapsed time: 0.645 seconds. 16 | 17 | tnom = 27 18 | temp = 27 19 | method = modified trap 20 | totiter = 23021 21 | traniter = 23018 22 | tranpoints = 6033 23 | accept = 4796 24 | rejected = 1237 25 | matrix size = 3 26 | fillins = 0 27 | solver = Normal 28 | Avg thread counts: 1.3/2.0/2.2/1.3 29 | Matrix Compiler1: off [0.1]/0.1/0.1 30 | Matrix Compiler2: off [0.0]/0.1/0.1 31 | 32 | -------------------------------------------------------------------------------- /examples/testfiles/testfile.net: -------------------------------------------------------------------------------- 1 | * C:\sandbox\PyLTSpice\examples\testfiles\testfile.asc 2 | V1 in 0 PULSE(-1 1 1u 1n 1n 2m 1m 1) 3 | R1 out in {res} 4 | C1 out 0 {cap} 5 | C2 out in {cap} 6 | .tran 3m 7 | .param cap=1n res=1k 8 | .step dec param cap 1p 10u 1 9 | .backanno 10 | .end 11 | -------------------------------------------------------------------------------- /examples/testfiles/testfile.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nunobrum/PyLTSpice/b1f8757dba564313d2eabfc5b117da0a8d80d133/examples/testfiles/testfile.raw -------------------------------------------------------------------------------- /examples/testfiles/testfile_ngspice.net: -------------------------------------------------------------------------------- 1 | * C:\sandbox\PyLTSpice_GitHub_nunobrum\tests\rc_example.asc 2 | V1 in 0 10V 3 | R1 out in 1k 4 | C1 out 0 1u 5 | .op 6 | .print op V(out) 7 | .end 8 | -------------------------------------------------------------------------------- /examples/testfiles/voltage.asy: -------------------------------------------------------------------------------- 1 | Version 4 2 | SymbolType CELL 3 | LINE Normal -8 36 8 36 4 | LINE Normal -8 76 8 76 5 | LINE Normal 0 28 0 44 6 | LINE Normal 0 96 0 88 7 | LINE Normal 0 16 0 24 8 | CIRCLE Normal -32 24 32 88 9 | WINDOW 0 24 16 Left 2 10 | WINDOW 3 24 96 Left 2 11 | SYMATTR Value V 12 | SYMATTR Prefix V 13 | SYMATTR Description Voltage Source, either DC, AC, PULSE, SINE, PWL, EXP, or SFFM 14 | PIN 0 16 NONE 0 15 | PINATTR PinName + 16 | PINATTR SpiceOrder 1 17 | PIN 0 96 NONE 0 18 | PINATTR PinName - 19 | PINATTR SpiceOrder 2 20 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD="C:\Apps\Python38\Scripts\sphinx-build.exe" 9 | ) 10 | set SOURCEDIR=doc 11 | set BUILDDIR=doc_build 12 | REM set PYTHONPATH=C:\Apps\Python38 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=42", 4 | "wheel" 5 | ] 6 | build-backend = "setuptools.build_meta" 7 | [project] 8 | name = "PyLTSpice" 9 | version = "5.4.2" 10 | authors = [ 11 | { name="Nuno Brum", email="me@nunobrum.com" }, 12 | ] 13 | description = "A set of tools to Automate LTSpice simulations" 14 | readme = "README.md" 15 | license = { file="LICENSE" } 16 | requires-python = ">=3.8" 17 | dependencies = [ 18 | "spicelib>=1.4.1", 19 | ] 20 | classifiers=[ 21 | "Programming Language :: Python :: 3", 22 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 23 | "Operating System :: OS Independent", 24 | ] 25 | 26 | [tool.setuptools.packages.find] 27 | # All the following settings are optional: 28 | where = ["."] # ["."] by default 29 | include = ["*"] # ["*"] by default 30 | exclude = [".idea", "doc", "doc_build", ".vscode", ".git"] # empty by default 31 | namespaces = true # true by default 32 | 33 | [project.urls] 34 | "Homepage" = "https://github.com/nunobrum/PyLTSpice" 35 | "Bug Tracker" = "https://github.com/nunobrum/PyLTSpice/issues" 36 | "Repository" = "https://github.com/nunobrum/PyLTSpice.git" 37 | "Author" = "https://www.nunobrum.com/" -------------------------------------------------------------------------------- /unittests/golden/test_components_output.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 880 680 3 | WIRE 208 80 128 80 4 | WIRE 288 80 208 80 5 | WIRE 288 128 288 80 6 | WIRE 128 192 128 80 7 | WIRE 288 224 288 208 8 | WIRE 368 224 288 224 9 | WIRE 448 224 368 224 10 | WIRE 288 240 288 224 11 | WIRE 128 352 128 272 12 | WIRE 288 352 288 320 13 | WIRE 288 352 128 352 14 | WIRE 448 352 448 288 15 | WIRE 448 352 288 352 16 | WIRE 128 368 128 352 17 | FLAG 128 368 0 18 | FLAG 368 224 out 19 | FLAG 208 80 in 20 | SYMBOL voltage 128 176 R0 21 | WINDOW 123 0 0 Left 0 22 | WINDOW 39 0 0 Left 0 23 | WINDOW 0 -82 53 Left 2 24 | WINDOW 3 -85 83 Left 2 25 | SYMATTR InstName Vin 26 | SYMATTR Value 1 27 | SYMBOL res 272 112 R0 28 | SYMATTR InstName R1 29 | SYMATTR Value 33k 30 | SYMBOL res 272 224 R0 31 | SYMATTR InstName R2 32 | SYMATTR Value {res} 33 | SYMBOL diode 432 224 R0 34 | WINDOW 0 39 36 Left 2 35 | SYMATTR InstName D1 36 | TEXT 56 488 Left 2 !.dc Vin 1 10 9 37 | TEXT 56 440 Left 2 !.param res=10k 38 | TEXT 120 400 Left 2 !.step temp 0 100 50 39 | TEXT 408 400 Left 2 ;.step param res 1k 16k 5k 40 | TEXT 56 512 Left 2 !.op 41 | TEXT 296 488 Left 2 !.param temp = 0 42 | -------------------------------------------------------------------------------- /unittests/golden/test_components_output_1.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 880 680 3 | WIRE 208 80 128 80 4 | WIRE 288 80 208 80 5 | WIRE 288 128 288 80 6 | WIRE 128 192 128 80 7 | WIRE 288 224 288 208 8 | WIRE 368 224 288 224 9 | WIRE 448 224 368 224 10 | WIRE 288 240 288 224 11 | WIRE 128 352 128 272 12 | WIRE 288 352 288 320 13 | WIRE 288 352 128 352 14 | WIRE 448 352 448 288 15 | WIRE 448 352 288 352 16 | WIRE 128 368 128 352 17 | FLAG 128 368 0 18 | FLAG 368 224 out 19 | FLAG 208 80 in 20 | SYMBOL voltage 128 176 R0 21 | WINDOW 123 0 0 Left 0 22 | WINDOW 39 0 0 Left 0 23 | WINDOW 0 -82 53 Left 2 24 | WINDOW 3 -85 83 Left 2 25 | SYMATTR InstName Vin 26 | SYMATTR Value 1 27 | SYMBOL res 272 224 R0 28 | SYMATTR InstName R2 29 | SYMATTR Value {res} 30 | SYMBOL diode 432 224 R0 31 | WINDOW 0 39 36 Left 2 32 | SYMATTR InstName D1 33 | TEXT 56 488 Left 2 !.dc Vin 1 10 9 34 | TEXT 56 440 Left 2 !.param res=10k 35 | TEXT 120 400 Left 2 !.step temp 0 100 50 36 | TEXT 408 400 Left 2 ;.step param res 1k 16k 5k 37 | TEXT 56 512 Left 2 !.op 38 | TEXT 296 488 Left 2 !.param temp = 0 39 | -------------------------------------------------------------------------------- /unittests/golden/test_instructions_output.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 880 680 3 | WIRE 208 80 128 80 4 | WIRE 288 80 208 80 5 | WIRE 288 128 288 80 6 | WIRE 128 192 128 80 7 | WIRE 288 224 288 208 8 | WIRE 368 224 288 224 9 | WIRE 448 224 368 224 10 | WIRE 288 240 288 224 11 | WIRE 128 352 128 272 12 | WIRE 288 352 288 320 13 | WIRE 288 352 128 352 14 | WIRE 448 352 448 288 15 | WIRE 448 352 288 352 16 | WIRE 128 368 128 352 17 | FLAG 128 368 0 18 | FLAG 368 224 out 19 | FLAG 208 80 in 20 | SYMBOL voltage 128 176 R0 21 | WINDOW 123 0 0 Left 0 22 | WINDOW 39 0 0 Left 0 23 | WINDOW 0 -82 53 Left 2 24 | WINDOW 3 -85 83 Left 2 25 | SYMATTR InstName Vin 26 | SYMATTR Value 1 27 | SYMBOL res 272 112 R0 28 | SYMATTR InstName R1 29 | SYMATTR Value 10k 30 | SYMBOL res 272 224 R0 31 | SYMATTR InstName R2 32 | SYMATTR Value {res} 33 | SYMBOL diode 432 224 R0 34 | WINDOW 0 39 36 Left 2 35 | SYMATTR InstName D1 36 | TEXT 56 488 Left 2 !.ac dec 10 1 100k 37 | TEXT 56 440 Left 2 !.param res=10k 38 | TEXT 120 400 Left 2 !.step temp 0 100 50 39 | TEXT 408 400 Left 2 ;.step param res 1k 16k 5k 40 | TEXT 56 512 Left 2 !.op 41 | TEXT 296 488 Left 2 !.param temp = 0 42 | TEXT 56 536 Left 2 !.save V(vout) 43 | TEXT 56 560 Left 2 !.save I(R1) 44 | TEXT 56 584 Left 2 !.save I(R2) 45 | TEXT 56 608 Left 2 !.save I(D1) 46 | -------------------------------------------------------------------------------- /unittests/golden/test_instructions_output_1.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 880 680 3 | WIRE 208 80 128 80 4 | WIRE 288 80 208 80 5 | WIRE 288 128 288 80 6 | WIRE 128 192 128 80 7 | WIRE 288 224 288 208 8 | WIRE 368 224 288 224 9 | WIRE 448 224 368 224 10 | WIRE 288 240 288 224 11 | WIRE 128 352 128 272 12 | WIRE 288 352 288 320 13 | WIRE 288 352 128 352 14 | WIRE 448 352 448 288 15 | WIRE 448 352 288 352 16 | WIRE 128 368 128 352 17 | FLAG 128 368 0 18 | FLAG 368 224 out 19 | FLAG 208 80 in 20 | SYMBOL voltage 128 176 R0 21 | WINDOW 123 0 0 Left 0 22 | WINDOW 39 0 0 Left 0 23 | WINDOW 0 -82 53 Left 2 24 | WINDOW 3 -85 83 Left 2 25 | SYMATTR InstName Vin 26 | SYMATTR Value 1 27 | SYMBOL res 272 112 R0 28 | SYMATTR InstName R1 29 | SYMATTR Value 10k 30 | SYMBOL res 272 224 R0 31 | SYMATTR InstName R2 32 | SYMATTR Value {res} 33 | SYMBOL diode 432 224 R0 34 | WINDOW 0 39 36 Left 2 35 | SYMATTR InstName D1 36 | TEXT 56 488 Left 2 !.ac dec 10 1 100k 37 | TEXT 56 440 Left 2 !.param res=10k 38 | TEXT 120 400 Left 2 !.step temp 0 100 50 39 | TEXT 408 400 Left 2 ;.step param res 1k 16k 5k 40 | TEXT 56 512 Left 2 !.op 41 | TEXT 296 488 Left 2 !.param temp = 0 42 | TEXT 56 536 Left 2 !.save V(vout) 43 | TEXT 56 584 Left 2 !.save I(R2) 44 | TEXT 56 608 Left 2 !.save I(D1) 45 | -------------------------------------------------------------------------------- /unittests/golden/test_parameter_output.asc: -------------------------------------------------------------------------------- 1 | Version 4 2 | SHEET 1 880 680 3 | WIRE 208 80 128 80 4 | WIRE 288 80 208 80 5 | WIRE 288 128 288 80 6 | WIRE 128 192 128 80 7 | WIRE 288 224 288 208 8 | WIRE 368 224 288 224 9 | WIRE 448 224 368 224 10 | WIRE 288 240 288 224 11 | WIRE 128 352 128 272 12 | WIRE 288 352 288 320 13 | WIRE 288 352 128 352 14 | WIRE 448 352 448 288 15 | WIRE 448 352 288 352 16 | WIRE 128 368 128 352 17 | FLAG 128 368 0 18 | FLAG 368 224 out 19 | FLAG 208 80 in 20 | SYMBOL voltage 128 176 R0 21 | WINDOW 123 0 0 Left 0 22 | WINDOW 39 0 0 Left 0 23 | WINDOW 0 -82 53 Left 2 24 | WINDOW 3 -85 83 Left 2 25 | SYMATTR InstName Vin 26 | SYMATTR Value 1 27 | SYMBOL res 272 112 R0 28 | SYMATTR InstName R1 29 | SYMATTR Value 10k 30 | SYMBOL res 272 224 R0 31 | SYMATTR InstName R2 32 | SYMATTR Value {res} 33 | SYMBOL diode 432 224 R0 34 | WINDOW 0 39 36 Left 2 35 | SYMATTR InstName D1 36 | TEXT 56 488 Left 2 !.dc Vin 1 10 9 37 | TEXT 56 440 Left 2 !.param res=10k 38 | TEXT 120 400 Left 2 !.step temp 0 100 50 39 | TEXT 408 400 Left 2 ;.step param res 1k 16k 5k 40 | TEXT 56 512 Left 2 !.op 41 | TEXT 296 488 Left 2 !.param temp = 25 42 | -------------------------------------------------------------------------------- /unittests/sweep_iterators_unittest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | @author: Andreas Kaeberlein 4 | @copyright: Copyright 2021 5 | @credits: AKAE 6 | 7 | @license: GPLv3 8 | @maintainer: Andreas Kaeberlein 9 | @email: andreas.kaeberlein@web.de 10 | 11 | @file: sweep_iterators_unittest.py 12 | @date: 2021-01-09 13 | 14 | @note 'sweep_iterators.py' unit test 15 | run ./test/unit/sweep_iterators/sweep_iterators_unittest.py 16 | """ 17 | 18 | 19 | 20 | #------------------------------------------------------------------------------ 21 | # Python Libs 22 | import sys # python path handling 23 | import os # platform independent paths 24 | import unittest # performs test 25 | # 26 | # Module libs 27 | sys.path.append(os.path.abspath((os.path.dirname(os.path.abspath(__file__)) + "/../"))) # add project root to lib search path 28 | from PyLTSpice.utils.sweep_iterators import sweep, sweep_n, sweep_log, sweep_log_n, sweep_iterators # Python Script under test 29 | #------------------------------------------------------------------------------ 30 | 31 | 32 | 33 | #------------------------------------------------------------------------------ 34 | class test_sweep_iterators(unittest.TestCase): 35 | 36 | #***************************** 37 | def setUp(self): 38 | """ 39 | @note set-ups test 40 | """ 41 | #***************************** 42 | 43 | 44 | #***************************** 45 | def test_init(self): 46 | """ 47 | @note inits class 48 | """ 49 | # prepare 50 | dut = sweep_iterators() 51 | # check 52 | self.assertEqual(dut.numTotalIterations, 0) 53 | self.assertEqual(dut.numCurrentIteration, 0) 54 | self.assertListEqual(dut.iteratorEntrys, []) 55 | self.assertListEqual(dut.idxForNextIter, []) 56 | 57 | 58 | #***************************** 59 | def test_add(self): 60 | """ 61 | @note add 62 | """ 63 | # prepare 64 | dut = sweep_iterators() 65 | # add entries 66 | dut.add('elem1', [1, 2, 3]) 67 | dut.add('elem2', [1.5, 1.8, 2.0]) 68 | dut.add('elem3', ["entry1", "entry2"]) 69 | # check 70 | self.assertEqual(dut.numTotalIterations, 18) 71 | self.assertEqual(dut.numCurrentIteration, 0) 72 | self.assertDictEqual(dut.iteratorEntrys[0], {'name': 'elem1', 'values': [1, 2, 3]}) 73 | self.assertDictEqual(dut.iteratorEntrys[1], {'name': 'elem2', 'values': [1.5, 1.8, 2.0]}) 74 | self.assertDictEqual(dut.iteratorEntrys[2], {'name': 'elem3', 'values': ['entry1', 'entry2']}) 75 | #***************************** 76 | 77 | 78 | #***************************** 79 | def test_next(self): 80 | """ 81 | @note next 82 | """ 83 | # prepare 84 | dut = sweep_iterators() 85 | dut.add('e1', [10]) 86 | dut.add('e2', [1e-6, 3e-6]) 87 | dut.add('e3', [1e3, 3e3, 5e3]) 88 | # caclulate iterators & check 89 | self.assertEqual(dut.numTotalIterations, 6) 90 | self.assertEqual(dut.numCurrentIteration, 0) 91 | self.assertDictEqual(dut.next(), {'e1': 10, 'e2': 1e-06, 'e3': 1000.0}) 92 | self.assertDictEqual(dut.next(), {'e1': 10, 'e2': 1e-06, 'e3': 3000.0}) 93 | self.assertDictEqual(dut.next(), {'e1': 10, 'e2': 1e-06, 'e3': 5000.0}) 94 | self.assertDictEqual(dut.next(), {'e1': 10, 'e2': 3e-06, 'e3': 1000.0}) 95 | self.assertDictEqual(dut.next(), {'e1': 10, 'e2': 3e-06, 'e3': 3000.0}) 96 | self.assertDictEqual(dut.next(), {'e1': 10, 'e2': 3e-06, 'e3': 5000.0}) 97 | 98 | #***************************** 99 | def test_done(self): 100 | """ 101 | @note next 102 | """ 103 | # prepare 104 | dut = sweep_iterators() 105 | dut.add('e1', [10]) 106 | # check 107 | self.assertDictEqual(dut.next(), {'e1': 10}) 108 | self.assertTrue(dut.done()) 109 | #***************************** 110 | 111 | #------------------------------------------------------------------------------ 112 | 113 | def test_iterator_objects(self): 114 | """ 115 | @note iterator_objects 116 | """ 117 | # ***************************** 118 | # check 119 | self.assertListEqual(list(sweep(10)), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) 120 | self.assertListEqual(list(sweep(1, 8)), [1, 2, 3, 4, 5, 6, 7, 8]) 121 | self.assertListEqual(list(sweep(2, 8, 2)), [2, 4, 6, 8]) 122 | self.assertListEqual(list(sweep(2, 8, -2)), [8, 6, 4, 2]) 123 | self.assertListEqual(list(sweep(8, 2, 2)), [8, 6, 4, 2]) 124 | self.assertListEqual(list(sweep(0.3, 1.1, 0.2)), [0.3, 0.5, 0.7, 0.9000000000000001, 1.1]) 125 | self.assertListEqual(list(sweep(15, -15, 2.5)), 126 | [15.0, 12.5, 10.0, 7.5, 5.0, 2.5, 0.0, -2.5, -5.0, -7.5, -10.0, -12.5, -15.0]) 127 | self.assertListEqual(list(sweep(-2, 2, 2)), [-2, 0, 2]) 128 | self.assertListEqual(list(sweep(-2, 2, -2)), [2, 0, -2]) 129 | self.assertListEqual(list(sweep(2, -2, 2)), [2, 0, -2]) 130 | self.assertListEqual(list(sweep(2, -2, -2)), [2, 0, -2]) 131 | self.assertListEqual(list(sweep_n(0.3, 1.1, 5)), [0.3, 0.5, 0.7, 0.9000000000000001, 1.1]) 132 | self.assertListEqual(list(sweep_n(15, -15, 13)), 133 | [15.0, 12.5, 10.0, 7.5, 5.0, 2.5, 0.0, -2.5, -5.0, -7.5, -10.0, -12.5, -15.0]) 134 | self.assertListEqual(list(sweep_log(0.1, 11e3, 10)), [0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0]) 135 | self.assertListEqual(list(sweep_log(1000, 1, 2)), 136 | [1000, 500.0, 250.0, 125.0, 62.5, 31.25, 15.625, 7.8125, 3.90625, 1.953125]) 137 | for a, b in zip(list(sweep_log_n(1, 10, 6)), 138 | [1.0, 1.584893192461113, 2.5118864315095806, 3.981071705534973, 6.309573444801934, 139 | 10.0]): 140 | 141 | self.assertAlmostEqual(a, b) 142 | for a, b in zip(list(sweep_log_n(10, 1, 5)), 143 | [10.0, 5.623413251903491, 3.1622776601683795, 1.7782794100389228, 1]): 144 | self.assertAlmostEqual(a, b) 145 | 146 | 147 | #------------------------------------------------------------------------------ 148 | if __name__ == '__main__': 149 | unittest.main() 150 | #------------------------------------------------------------------------------ 151 | -------------------------------------------------------------------------------- /unittests/test_asc_editor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | sys.path.append( 6 | os.path.abspath((os.path.dirname(os.path.abspath(__file__)) + "/../"))) # add project root to lib search path 7 | 8 | import PyLTSpice 9 | 10 | test_dir = '../examples/testfiles/' if os.path.abspath(os.curdir).endswith('unittests') else './examples/testfiles/' 11 | golden_dir = './golden/' if os.path.abspath(os.curdir).endswith('unittests') else './unittests/golden/' 12 | 13 | 14 | class ASC_Editor_Test(unittest.TestCase): 15 | 16 | def setUp(self): 17 | self.edt = PyLTSpice.AscEditor(test_dir + "DC sweep.asc") 18 | 19 | def test_component_editing(self): 20 | self.assertEqual(self.edt.get_component_value('R1'), '10k', "Tested R1 Value") # add assertion here 21 | self.assertListEqual(self.edt.get_components(), ['Vin', 'R1', 'R2', 'D1'], 22 | "Tested get_components") # add assertion here 23 | self.edt.set_component_value('R1', '33k') 24 | self.edt.save_netlist(test_dir + 'test_components_output.asc') 25 | self.equalFiles(test_dir + 'test_components_output.asc', golden_dir + 'test_components_output.asc') 26 | self.assertEqual(self.edt.get_component_value('R1'), '33k', "Tested R1 Value") # add assertion here 27 | self.edt.remove_component('R1') 28 | self.edt.save_netlist(test_dir + 'test_components_output_1.asc') 29 | self.equalFiles(test_dir + 'test_components_output_1.asc', golden_dir + 'test_components_output_1.asc') 30 | 31 | def test_parameter_edit(self): 32 | self.assertEqual(self.edt.get_parameter('TEMP'), '0', "Tested TEMP Parameter") # add assertion here 33 | self.edt.set_parameter('TEMP', 25) 34 | self.assertEqual(self.edt.get_parameter('TEMP'), '25', "Tested TEMP Parameter") # add assertion here 35 | self.edt.save_netlist(test_dir + 'test_parameter_output.asc') 36 | self.equalFiles(test_dir + 'test_parameter_output.asc', golden_dir + 'test_parameter_output.asc') 37 | self.edt.set_parameter('TEMP', 0) # reset to 0 38 | self.assertEqual(self.edt.get_parameter('TEMP'), '0', "Tested TEMP Parameter") # add assertion here 39 | 40 | def test_instructions(self): 41 | self.edt.add_instruction('.ac dec 10 1 100k') 42 | self.edt.add_instruction('.save V(vout)') 43 | self.edt.add_instruction('.save I(R1)') 44 | self.edt.add_instruction('.save I(R2)') 45 | self.edt.add_instruction('.save I(D1)') 46 | self.edt.save_netlist(test_dir + 'test_instructions_output.asc') 47 | self.equalFiles(test_dir + 'test_instructions_output.asc', golden_dir + 'test_instructions_output.asc') 48 | self.edt.remove_instruction('.save I(R1)') 49 | self.edt.save_netlist(test_dir + 'test_instructions_output_1.asc') 50 | self.equalFiles(test_dir + 'test_instructions_output_1.asc', golden_dir + 'test_instructions_output_1.asc') 51 | 52 | def equalFiles(self, file1, file2): 53 | with open(file1, 'r') as f1: 54 | lines1 = f1.readlines() 55 | with open(file2, 'r') as f2: 56 | lines2 = f2.readlines() 57 | for i, lines in enumerate(zip(lines1, lines2)): 58 | self.assertEqual(lines[0], lines[1], "Line %d" % i) 59 | 60 | 61 | if __name__ == '__main__': 62 | unittest.main() 63 | -------------------------------------------------------------------------------- /unittests/test_pyltspice.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | # ------------------------------------------------------------------------------- 5 | # ____ _ _____ ____ _ 6 | # | _ \ _ _| | |_ _/ ___| _ __ (_) ___ ___ 7 | # | |_) | | | | | | | \___ \| '_ \| |/ __/ _ \ 8 | # | __/| |_| | |___| | ___) | |_) | | (_| __/ 9 | # |_| \__, |_____|_| |____/| .__/|_|\___\___| 10 | # |___/ |_| 11 | # 12 | # Name: test_pyltspice.py 13 | # Purpose: Tool used to launch LTSpice simulation in batch mode. Netlsts can 14 | # be updated by user instructions 15 | # 16 | # Author: Nuno Brum (nuno.brum@gmail.com) 17 | # 18 | # Licence: refer to the LICENSE file 19 | # ------------------------------------------------------------------------------- 20 | """ 21 | @author: Nuno Brum 22 | @copyright: Copyright 2022 23 | @credits: nunobrum 24 | 25 | @license: GPLv3 26 | @maintainer: Nuno Brum 27 | @email: me@nunobrum.com 28 | 29 | @file: test_pyltspice.py 30 | @date: 2022-09-19 31 | 32 | @note pyltspice ltsteps + sim_commander + raw_read unit test 33 | run ./test/unittests/test_pyltspice 34 | """ 35 | 36 | import os # platform independent paths 37 | # ------------------------------------------------------------------------------ 38 | # Python Libs 39 | import sys # python path handling 40 | import unittest # performs test 41 | 42 | # 43 | # Module libs 44 | 45 | sys.path.append( 46 | os.path.abspath((os.path.dirname(os.path.abspath(__file__)) + "/../"))) # add project root to lib search path 47 | from PyLTSpice.log.ltsteps import LTSpiceLogReader 48 | from PyLTSpice.sim.sim_batch import SimCommander 49 | from PyLTSpice.raw.raw_read import RawRead 50 | from PyLTSpice.editor.spice_editor import SpiceEditor 51 | from PyLTSpice.sim.sim_runner import SimRunner 52 | 53 | def has_ltspice_detect(): 54 | from PyLTSpice.sim.ltspice_simulator import LTspice 55 | ltspice = LTspice 56 | return (isinstance(ltspice.spice_exe, list) and 57 | len(ltspice.spice_exe) > 0 and 58 | any(os.path.exists(exe) for exe in ltspice.spice_exe)) 59 | 60 | 61 | # ------------------------------------------------------------------------------ 62 | has_ltspice = has_ltspice_detect() 63 | skip_ltspice_tests = not has_ltspice 64 | print("skip_ltspice_tests", skip_ltspice_tests) 65 | test_dir = '../examples/testfiles/' if os.path.abspath(os.curdir).endswith('unittests') else './examples/testfiles/' 66 | # test_dir = os.path.abspath(test_dir) 67 | print("test_dir", test_dir) 68 | # ------------------------------------------------------------------------------ 69 | 70 | # if has_ltspice: 71 | # os.chdir(os.path.abspath((os.path.dirname(os.path.abspath(__file__))))) 72 | 73 | 74 | class test_pyltspice(unittest.TestCase): 75 | """Unnittesting PyLTSpice""" 76 | # ***************************** 77 | @unittest.skipIf(skip_ltspice_tests, "Skip if not in windows environment") 78 | def test_batch_test(self): 79 | """ 80 | @note inits class 81 | """ 82 | # prepare 83 | self.sim_files = [] 84 | self.measures = {} 85 | 86 | def processing_data(raw_file, log_file): 87 | print("Handling the simulation data of %s, log file %s" % (raw_file, log_file)) 88 | self.sim_files.append((raw_file, log_file)) 89 | 90 | # select spice model 91 | LTC = SimCommander(test_dir + "Batch_Test.asc") 92 | LTC.set_parameters(res=0, cap=100e-6) 93 | LTC.set_component_value('R2', '2k') # Modifying the value of a resistor 94 | LTC.set_component_value('R1', '4k') 95 | LTC.set_element_model('V3', "SINE(0 1 3k 0 0 0)") # Modifying the 96 | LTC.set_component_value('XU1:C2', 20e-12) # modifying a 97 | # define simulation 98 | LTC.add_instructions( 99 | "; Simulation settings", 100 | # ".step dec param freq 10k 1Meg 10", 101 | ) 102 | LTC.set_parameter("run", "0") 103 | 104 | for opamp in ('AD712', 'AD820'): 105 | LTC.set_element_model('XU1', opamp) 106 | for supply_voltage in (5, 10, 15): 107 | LTC.set_component_value('V1', supply_voltage) 108 | LTC.set_component_value('V2', -supply_voltage) 109 | # overriding the automatic netlist naming 110 | run_netlist_file = "{}_{}_{}.net".format(LTC.circuit_file.name, opamp, supply_voltage) 111 | LTC.run(run_filename=run_netlist_file, callback=processing_data) 112 | 113 | LTC.wait_completion() 114 | 115 | # Sim Statistics 116 | print('Successful/Total Simulations: ' + str(LTC.okSim) + '/' + str(LTC.runno)) 117 | self.assertEqual(LTC.okSim, 6) 118 | self.assertEqual(LTC.runno, 6) 119 | 120 | # check 121 | LTC.reset_netlist() 122 | LTC.set_element_model('V3', "AC 1 0") 123 | LTC.add_instructions( 124 | "; Simulation settings", 125 | ".ac dec 30 1 10Meg", 126 | ".meas AC GainAC MAX mag(V(out)) ; find the peak response and call it ""Gain""", 127 | ".meas AC FcutAC TRIG mag(V(out))=GainAC/sqrt(2) FALL=last" 128 | ) 129 | 130 | raw_file, log_file = LTC.run(run_filename="no_callback.net").wait_results() 131 | LTC.wait_completion() 132 | print("no_callback", raw_file, log_file) 133 | log = LTSpiceLogReader(log_file) 134 | for measure in log.get_measure_names(): 135 | print(measure, '=', log.get_measure_value(measure)) 136 | self.assertEqual(log.get_measure_value('fcutac'), 8479370.0) 137 | vout1m = log.get_measure_value('vout1m') 138 | self.assertEqual(vout1m.mag_db(), 6.02059) 139 | self.assertAlmostEqual(vout1m.ph_rad(), 0, 5) 140 | self.assertEqual(log.get_measure_value('vout1m').mag_db(), 6.02059) 141 | 142 | @unittest.skipIf(skip_ltspice_tests, "Skip if not in windows environment") 143 | def test_run_from_spice_editor(self): 144 | """Run command on SpiceEditor""" 145 | LTC = SimRunner(output_folder=test_dir + "temp/") 146 | # select spice model 147 | LTC.create_netlist(test_dir + "testfile.asc") 148 | netlist = SpiceEditor(test_dir + "testfile.net") 149 | # set default arguments 150 | netlist.set_parameters(res=0.001, cap=100e-6) 151 | # define simulation 152 | netlist.add_instructions( 153 | "; Simulation settings", 154 | # [".STEP PARAM Rmotor LIST 21 28"], 155 | ".TRAN 3m", 156 | # ".step param run 1 2 1" 157 | ) 158 | # do parameter sweep 159 | for res in range(5): 160 | # LTC.runs_to_do = range(2) 161 | netlist.set_parameters(ANA=res) 162 | raw, log = LTC.run(netlist).wait_results() 163 | print("Raw file '%s' | Log File '%s'" % (raw, log)) 164 | # Sim Statistics 165 | print('Successful/Total Simulations: ' + str(LTC.okSim) + '/' + str(LTC.runno)) 166 | 167 | @unittest.skipIf(skip_ltspice_tests, "Skip if not in windows environment") 168 | def test_sim_runner(self): 169 | """SimRunner and SpiceEditor singletons""" 170 | # Old legacy class that merged SpiceEditor and SimRunner 171 | def callback_function(raw_file, log_file): 172 | print("Handling the simulation data of %s, log file %s" % (raw_file, log_file)) 173 | 174 | LTC = SimRunner(output_folder=test_dir + "temp/") 175 | SE = SpiceEditor(test_dir + "testfile.net") 176 | #, parallel_sims=1) 177 | tstart = 0 178 | for tstop in (2, 5, 8, 10): 179 | tduration = tstop - tstart 180 | SE.add_instruction(".tran {}".format(tduration), ) 181 | if tstart != 0: 182 | SE.add_instruction(".loadbias {}".format(bias_file)) 183 | # Put here your parameter modifications 184 | # LTC.set_parameters(param1=1, param2=2, param3=3) 185 | bias_file = test_dir + "sim_loadbias_%d.txt" % tstop 186 | SE.add_instruction(".savebias {} internal time={}".format(bias_file, tduration)) 187 | tstart = tstop 188 | LTC.run(SE, callback=callback_function) 189 | 190 | SE.reset_netlist() 191 | SE.add_instruction('.ac dec 40 1m 1G') 192 | SE.set_component_value('V1', 'AC 1 0') 193 | LTC.run(SE, callback=callback_function) 194 | LTC.wait_completion() 195 | 196 | @unittest.skipIf(False, "Execute All") 197 | def test_ltsteps_measures(self): 198 | """LTSteps Measures from Batch_Test.asc""" 199 | assert_data = { 200 | 'vout1m' : [ 201 | -0.0186257, 202 | -1.04378, 203 | -1.64283, 204 | -0.622014, 205 | 1.32386, 206 | -1.35125, 207 | -1.88222, 208 | 1.28677, 209 | 1.03154, 210 | 0.953548, 211 | -0.192821, 212 | -1.42535, 213 | 0.451607, 214 | 0.0980979, 215 | 1.55525, 216 | 1.66809, 217 | 0.11246, 218 | 0.424023, 219 | -1.30035, 220 | 0.614292, 221 | -0.878185, 222 | ], 223 | 'vin_rms' : [ 224 | 0.706221, 225 | 0.704738, 226 | 0.708225, 227 | 0.707042, 228 | 0.704691, 229 | 0.704335, 230 | 0.704881, 231 | 0.703097, 232 | 0.70322, 233 | 0.703915, 234 | 0.703637, 235 | 0.703558, 236 | 0.703011, 237 | 0.702924, 238 | 0.702944, 239 | 0.704121, 240 | 0.704544, 241 | 0.704193, 242 | 0.704236, 243 | 0.703701, 244 | 0.703436, 245 | ], 246 | 'vout_rms' : [ 247 | 1.41109, 248 | 1.40729, 249 | 1.41292, 250 | 1.40893, 251 | 1.40159, 252 | 1.39763, 253 | 1.39435, 254 | 1.38746, 255 | 1.38807, 256 | 1.38933, 257 | 1.38759, 258 | 1.38376, 259 | 1.37771, 260 | 1.37079, 261 | 1.35798, 262 | 1.33252, 263 | 1.24314, 264 | 1.07237, 265 | 0.875919, 266 | 0.703003, 267 | 0.557131, 268 | 269 | ], 270 | 'gain' : [ 271 | 1.99809, 272 | 1.99689, 273 | 1.99502, 274 | 1.99271, 275 | 1.98894, 276 | 1.98432, 277 | 1.97814, 278 | 1.97336, 279 | 1.97387, 280 | 1.97372, 281 | 1.97202, 282 | 1.9668, 283 | 1.95973, 284 | 1.95012, 285 | 1.93184, 286 | 1.89246, 287 | 1.76445, 288 | 1.52284, 289 | 1.24379, 290 | 0.999007, 291 | 0.792014, 292 | ], 293 | 'period' : [ 294 | 0.000100148, 295 | 7.95811e-005, 296 | 6.32441e-005, 297 | 5.02673e-005, 298 | 3.99594e-005, 299 | 3.1772e-005, 300 | 2.52675e-005, 301 | 2.01009e-005, 302 | 1.59975e-005, 303 | 1.27418e-005, 304 | 1.01541e-005, 305 | 8.10036e-006, 306 | 6.47112e-006, 307 | 5.18241e-006, 308 | 4.16639e-006, 309 | 3.37003e-006, 310 | 2.75114e-006, 311 | 2.26233e-006, 312 | 1.85367e-006, 313 | 1.50318e-006, 314 | 1.20858e-006, 315 | 316 | ], 317 | 'period_at': [ 318 | 0.000100148, 319 | 7.95811e-005, 320 | 6.32441e-005, 321 | 5.02673e-005, 322 | 3.99594e-005, 323 | 3.1772e-005, 324 | 2.52675e-005, 325 | 2.01009e-005, 326 | 1.59975e-005, 327 | 1.27418e-005, 328 | 1.01541e-005, 329 | 8.10036e-006, 330 | 6.47112e-006, 331 | 5.18241e-006, 332 | 4.16639e-006, 333 | 3.37003e-006, 334 | 2.75114e-006, 335 | 2.26233e-006, 336 | 1.85367e-006, 337 | 1.50318e-006, 338 | 1.20858e-006, 339 | ] 340 | } 341 | if has_ltspice: 342 | LTC = SimCommander(test_dir + "Batch_Test.asc") 343 | raw_file, log_file = LTC.run().wait_results() 344 | print(raw_file, log_file) 345 | else: 346 | log_file = test_dir + "Batch_Test_1.log" 347 | log = LTSpiceLogReader(log_file) 348 | # raw = RawRead(raw_file) 349 | for measure in assert_data: 350 | print("measure", measure) 351 | for step in range(log.step_count): 352 | self.assertEqual(log.get_measure_value(measure, step), assert_data[measure][step]) 353 | 354 | print(log.get_measure_value(measure, step), assert_data[measure][step]) 355 | 356 | @unittest.skipIf(False, "Execute All") 357 | def test_operating_point(self): 358 | """Operating Point Simulation Test""" 359 | if has_ltspice: 360 | LTC = SimCommander(test_dir + "DC op point.asc") 361 | raw_file, log_file = LTC.run().wait_results() 362 | else: 363 | raw_file = test_dir + "DC op point_1.raw" 364 | # log_file = test_dir + "DC op point_1.log" 365 | raw = RawRead(raw_file) 366 | traces = [raw.get_trace(trace)[0] for trace in raw.get_trace_names()] 367 | 368 | self.assertListEqual(traces, [1.0, 0.5, 4.999999873689376e-05, 4.999999873689376e-05, -4.999999873689376e-05], "Lists are different") 369 | 370 | @unittest.skipIf(False, "Execute All") 371 | def test_operating_point_step(self): 372 | """Operating Point Simulation with Steps """ 373 | if has_ltspice: 374 | LTC = SimCommander(test_dir + "DC op point - STEP.asc") 375 | raw_file, log_file = LTC.run().wait_results() 376 | else: 377 | raw_file = test_dir + "DC op point - STEP_1.raw" 378 | raw = RawRead(raw_file) 379 | vin = raw.get_trace('V(in)') 380 | 381 | for i, b in enumerate(('V(in)', 'V(b4)', 'V(b3)', 'V(b2)', 'V(b1)', 'V(out)'),): 382 | meas = raw.get_trace(b) 383 | for step in range(raw.nPoints): 384 | self.assertEqual(meas[step], vin[step] * 2**-i) 385 | 386 | @unittest.skipIf(False, "Execute All") 387 | def test_transient(self): 388 | """Transient Simulation test """ 389 | if has_ltspice: 390 | LTC = SimCommander(test_dir + "TRAN.asc") 391 | raw_file, log_file = LTC.run().wait_results() 392 | else: 393 | raw_file = test_dir + "TRAN_1.raw" 394 | log_file = test_dir + "TRAN_1.log" 395 | raw = RawRead(raw_file) 396 | log = LTSpiceLogReader(log_file) 397 | vout = raw.get_trace('V(out)') 398 | meas = ('t1', 't2', 't3', 't4', 't5',) 399 | time = (1e-3, 2e-3, 3e-3, 4e-3, 5e-3,) 400 | for m, t in zip(meas, time): 401 | log_value = log.get_measure_value(m) 402 | raw_value = vout.get_point_at(t) 403 | print(log_value, raw_value, log_value - raw_value) 404 | self.assertAlmostEqual(log_value, raw_value, 2, "Mismatch between log file and raw file") 405 | 406 | @unittest.skipIf(False, "Execute All") 407 | def test_transient_steps(self): 408 | """Transient simulation with stepped data.""" 409 | if has_ltspice: 410 | LTC = SimCommander(test_dir + "TRAN - STEP.asc") 411 | raw_file, log_file = LTC.run().wait_results() 412 | else: 413 | raw_file = test_dir + "TRAN - STEP_1.raw" 414 | log_file = test_dir + "TRAN - STEP_1.log" 415 | 416 | raw = RawRead(raw_file) 417 | log = LTSpiceLogReader(log_file) 418 | vout = raw.get_trace('V(out)') 419 | meas = ('t1', 't2', 't3', 't4', 't5',) 420 | time = (1e-3, 2e-3, 3e-3, 4e-3, 5e-3,) 421 | for m, t in zip(meas, time): 422 | print(m) 423 | for step, step_dict in enumerate(raw.steps): 424 | log_value = log.get_measure_value(m, step) 425 | raw_value = vout.get_point_at(t, step) 426 | print(step, step_dict, log_value, raw_value, log_value - raw_value) 427 | self.assertAlmostEqual(log_value, raw_value, 2, f"Mismatch between log file and raw file in step :{step_dict} measure: {m} ") 428 | 429 | @unittest.skipIf(False, "Execute All") 430 | def test_ac_analysis(self): 431 | """AC Analysis Test""" 432 | from numpy import pi, angle 433 | if has_ltspice: 434 | LTC = SimCommander(test_dir + "AC.asc") 435 | raw_file, log_file = LTC.run().wait_results() 436 | R1 = LTC.get_component_floatvalue('R1') 437 | C1 = LTC.get_component_floatvalue('C1') 438 | else: 439 | raw_file = test_dir + "AC_1.raw" 440 | log_file = test_dir + "AC_1.log" 441 | R1 = 100 442 | C1 = 10E-6 443 | # Compute the RC AC response with the resistor and capacitor values from the netlist. 444 | raw = RawRead(raw_file) 445 | vout_trace = raw.get_trace('V(out)') 446 | vin_trace = raw.get_trace('V(in)') 447 | for point, freq in enumerate(raw.axis): 448 | vout1 = vout_trace.get_point_at(freq) 449 | vout2 = vout_trace.get_point(point) 450 | vin = vin_trace.get_point(point) 451 | self.assertEqual(vout1, vout2) 452 | self.assertEqual(abs(vin), 1) 453 | # Calculate the magnitude of the answer Vout = Vin/(1+jwRC) 454 | h = vin/(1 + 2j * pi * freq * R1 * C1) 455 | self.assertAlmostEqual(abs(vout1), abs(h), 5, f"Difference between theoretical value ans simulation at point {point}") 456 | self.assertAlmostEqual(angle(vout1), angle(h), 5, f"Difference between theoretical value ans simulation at point {point}") 457 | 458 | @unittest.skipIf(False, "Execute All") 459 | def test_ac_analysis_steps(self): 460 | """AC Analysis Test with steps""" 461 | from numpy import pi, angle 462 | if has_ltspice: 463 | LTC = SimCommander(test_dir + "AC - STEP.asc") 464 | raw_file, log_file = LTC.run().wait_results() 465 | C1 = LTC.get_component_floatvalue('C1') 466 | else: 467 | raw_file = test_dir + "AC - STEP_1.raw" 468 | log_file = test_dir + "AC - STEP_1.log" 469 | C1 = 159.1549e-6 # 159.1549uF 470 | # Compute the RC AC response with the resistor and capacitor values from the netlist. 471 | raw = RawRead(raw_file) 472 | vin_trace = raw.get_trace('V(in)') 473 | vout_trace = raw.get_trace('V(out)') 474 | for step, step_dict in enumerate(raw.steps): 475 | R1 = step_dict['r1'] 476 | # print(step, step_dict) 477 | for point in range(0, raw.get_len(step), 10): # 10 times less points 478 | print(point, end=' - ') 479 | vout = vout_trace.get_point(point, step) 480 | vin = vin_trace.get_point(point, step) 481 | freq = raw.axis.get_point(point, step) 482 | # Calculate the magnitude of the answer Vout = Vin/(1+jwRC) 483 | h = vin/(1 + 2j * pi * freq * R1 * C1) 484 | # print(freq, vout, h, vout - h) 485 | self.assertAlmostEqual(abs(vout), abs(h), 5, 486 | f"Difference between theoretical value ans simulation at point {point}:") 487 | self.assertAlmostEqual(angle(vout), angle(h), 5, 488 | f"Difference between theoretical value ans simulation at point {point}") 489 | 490 | # 491 | # def test_pathlib(self): 492 | # """pathlib support""" 493 | # import pathlib 494 | # DIR = pathlib.Path("../tests") 495 | # raw_file = DIR / "AC - STEP_1.raw" 496 | # raw = RawRead(raw_file) 497 | 498 | 499 | # ------------------------------------------------------------------------------ 500 | if __name__ == '__main__': 501 | unittest.main() 502 | # ------------------------------------------------------------------------------ 503 | --------------------------------------------------------------------------------