Commit b94ee4f3 authored by Sean Leavey's avatar Sean Leavey

Merge branch 'release/0.5.7'

parents 9fc5cf04 f83c509f
Pipeline #46263 passed with stages
in 28 minutes and 25 seconds
......@@ -3,5 +3,6 @@
"files.trimTrailingWhitespace": true,
"editor.rulers": [100],
"editor.tabSize": 4,
"editor.insertSpaces": true
"editor.insertSpaces": true,
"python.pythonPath": "/home/sean/conda/envs/circuit/bin/python"
......@@ -4,7 +4,7 @@ import os
import sys
import logging
from unittest import TestSuite, TestLoader, TextTestRunner
from click import group, argument, option
import click
from zero import set_log_verbosity
from .validation import LisoTestSuite
......@@ -39,15 +39,15 @@ TESTS = {
"all-fast": ALL_FAST_TESTS
def tests():
"""Zero testing facility."""
@argument("suite_names", nargs=-1)
@option("-v", "--verbose", count=True, default=0,
help="Enable verbose output. Supply extra flag for greater verbosity, i.e. \"-vv\".")
@click.argument("suite_names", nargs=-1, required=True)
@click.option("-v", "--verbose", count=True, default=0,
help="Enable verbose output. Supply extra flag for greater verbosity, i.e. \"-vv\".")
def run(suite_names, verbose):
"""Run test suites."""
if verbose > 2:
......@@ -59,14 +59,21 @@ def run(suite_names, verbose):
set_log_verbosity(logging.WARNING - 10 * verbose, logger)
# test suite to run
suite = TestSuite([TESTS.get(suite_name) for suite_name in suite_names])
test_suites = [TESTS[suite_name] for suite_name in suite_names]
except KeyError as e:
click.echo("Suite name %s is invalid (use \"suites\" to list available suites)" % e,
print("Running %i tests" % suite.countTestCases())
suite = TestSuite(test_suites)
click.echo("Running %i tests" % suite.countTestCases())
run_and_exit(suite, verbosity=verbose)
def suites():
print(", ".join(TESTS))
click.echo(", ".join(TESTS))
def run_and_exit(suite, verbosity=1):
"""Run tests and exit with a status code representing the test result"""
"""Component library parser tests"""
import os
import json
import unittest
import numpy as np
from numpy.testing import assert_array_almost_equal as np_assert_array_almost_equal
from zero.config import OpAmpLibrary
class NullOpAmpLibrary(OpAmpLibrary):
"""Op-amp library which loads nothing by default, to facilitate incremental testing"""
def base_config_path(self):
"""Overridden base config path"""
return os.devnull
def user_config_path(self):
"""Overridden user config path"""
return os.devnull
def parse_opamp_test_data(self, name, data):
"""Wrapper to parse an op-amp as if it came from the YAML file"""
return self._parse_lib_data(name, data)
def find_library_opamp_by_test_name(self, name):
name = self.format_name(name)
for opamp in self.opamps:
if opamp.model == name:
return opamp
raise ValueError("op-amp '%s' not found" % name)
class OpAmpPolesTestCase(unittest.TestCase):
def setUp(self):
def reset(self):
"""Reset library"""
self.library = NullOpAmpLibrary()
def _parse_and_return(self, data):
"""Parse the specified op-amp data and return the corresponding library op-amp"""
# Generate unique name.
# Slow JSON serialisation used to avoid implementing our own hashing method:
name = "__testop__%s__" % hash(json.dumps(data))
# parse
self.library.parse_opamp_test_data(name, data)
# get parsed model
return self.library.find_library_opamp_by_test_name(name)
def test_single_real_poles(self):
"""Test op-amp with single real pole and zero is parsed properly"""
model = self._parse_and_return({"poles": ["53.4M"], "zeros": ["76.5M"]})
np_assert_array_almost_equal(np.array([53.4e6]), model.poles)
np_assert_array_almost_equal(np.array([76.5e6]), model.zeros)
def test_single_complex_poles(self):
"""Test op-amp with single complex pole and zero is parsed properly"""
model = self._parse_and_return({"poles": ["53.4M 5.1"], "zeros": ["76.5M 4.7"]})
np_assert_array_almost_equal(np.array([5235294.117647 - 53142748.287059j,
5235294.117647 + 53142748.287059j]),
np_assert_array_almost_equal(np.array([8138297.87234042 - 76065880.04973753j,
8138297.87234042 + 76065880.04973753j]),
def test_multiple_poles(self):
"""Test op-amp with multiple complex poles and zeros is parsed properly"""
model = self._parse_and_return({"poles": ["13M", "53.4M 5.1"],
"zeros": ["19.3M", "76.5M 4.7"]})
5235294.117647 - 53142748.287059j,
5235294.117647 + 53142748.287059j]),
8138297.87234042 - 76065880.04973753j,
8138297.87234042 + 76065880.04973753j]),
......@@ -293,7 +293,8 @@ class OpAmp(LibraryOpAmp, Component):
raise NoiseNotFoundError("inverting current noise")
def __str__(self):
return super().__str__() + " [in+={cmp.node1}, in-={cmp.node2}, out={cmp.node3}, model={cmp.model}]".format(cmp=self)
suffix = " [in+={cmp.node1}, in-={cmp.node2}, out={cmp.node3}, model={cmp.model}]".format(cmp=self)
return Component.__str__(self) + suffix
class Input(Component):
......@@ -38,9 +38,10 @@ class OpAmpLibrary(BaseConfig):
count = 0
# each section is a new op-amp
for opamp, data in self["op-amps"].items():
self._parse_lib_data(opamp, data)
count += 1
if "op-amps" in self and self["op-amps"] is not None:
for opamp, data in self["op-amps"].items():
self._parse_lib_data(opamp, data)
count += 1
LOGGER.debug("found %i op-amps", count)
......@@ -103,15 +104,19 @@ class OpAmpLibrary(BaseConfig):
def _parse_lib_data(self, name, data):
"""Parse op-amp data from config file"""
# handle poles and zeros
poles = []
zeros = []
if "poles" in data and data["poles"] is not None:
poles = [self._parse_freq_str(freq) for freq in data["poles"]]
poles = np.array([])
for freq in data["poles"]:
if "zeros" in data and data["zeros"] is not None:
zeros = [self._parse_freq_str(freq) for freq in data["zeros"]]
zeros = np.array([])
for freq in data["zeros"]:
poles = np.array(poles)
zeros = np.array(zeros)
# build op-amp data dict with poles and zeros as entries
class_data = {"zeros": zeros, "poles": poles}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment